Swiftの配列 Array型

今回はSwiftで配列のデータ構造を扱うときに利用するArray<Element>型についてまとめます。
配列といえば、複数の値を順番に保持することができ、各要素にインデックスでアクセスできるようにしたデータ構造のことですね。
様々なプログラミング言語で標準で実装されているデータ構造でもあり、Swiftでも実装されています。

Array<Element>型の定義

変数・定数の定義

型名による定義

Array<Element>型のElement型には実際に利用したい具体的な型を当てはめて利用します。
また、Element型にはどの型でも当てはめることができます。
Swiftの配列 Array<Element>型の変数・定数の定義は以下のように行います。

// Array<Int>型の定数
let intArray: Array<Int>

// Array<String>型の変数
var stringArray: Array<String>

糖衣構文による定義

Array<Element>型には”[Element]”という糖衣構文が用意されています。
糖衣構文を利用することで型名による定義よりも簡潔に定義することができます。

// Array<Int>型の定数
let intArray: [Int]

// Array<String>型の変数
let stringArray: [String]

配列リテラルによる値の生成

Array<Element>型の値を生成するには、[]内に値を,区切りで記述することでできます。

// Array<Element>型の値の生成
[値1, 値2, 値3, ... ]

こちらの記述方法で生成されたArray<Element>型を配列リテラルと言います。
配列リテラルを記述する際は、値の型が一致するように注意する必要があります。

// Array<Int>型の変数
var intArray: [Int]
intArray = [1, 2, 3] // [Int]の配列リテラルを代入
intArray = [1, 2, 3, "a"] // String型の値が混ざっているためコンパイルエラーとなる

型推論

Array<Element>型も型推論による代入が可能です。

// Array<Int>型への型推論
var intArray = [1, 2, 3]

// Array<String>型への型推論
let stringArray = ["abc", "def"]

ただし、空の配列の代入をする際は要素の型を推論できないため、型を指定する必要があります。

// 型推論不可のため定義する必要がある
var intArray: [Int] = []

// 型がないとコンパイルエラーとなる
let array = []

Any型の配列

Any型はあらゆる型の値を代入可能な型です。
そのため、Array<Any>型は複数の型の値を格納することができます。

// Array<Any>型は複数の型の値を格納可能
var intArray: [Any] = [1, "a", true, 1.1]

Array<Element>型の操作

要素へのアクセス

要素へのアクセスはインデックスを指定することで行います。
インデックスは各要素に採番される数字で1つ目の要素から順に0,1,2,3…と採番されていきます。
配列[インデックス]と記述することで配列の要素へアクセスすることができます。
ただし、存在しないインデックスを指定した場合実行時エラーとなります。

// インデックスは0,1,2となる
let stringArray = ["abc", "def", "ghi"]

let str0 = stringArray[0] // "abc"
let str1 = stringArray[1] // "def"
let str2 = stringArray[2] // "ghi"
let str3 = stringArray[3] // 値が存在しないインデックスを指定すると実行時エラーとなる

要素の変更

要素の変更を行う場合、上記の方法で要素へアクセスして値の代入を行うことでできます。

// インデックスは0,1,2となる
var stringArray = ["abc", "def", "ghi"]

let str0 = stringArray[0] // "abc"
let str1 = stringArray[1] // "def"
let str2 = stringArray[2] // "ghi"

// インデックス1の要素の値を変更
stringArray[1] = "DEF"
let STR1 = stringArray[1] // "DEF"

また定数の配列の値の変更は行えません。

// インデックスは0,1,2となる
let stringArray = ["abc", "def", "ghi"]

stringArray[1] = "DEF" // 定数の配列の値の変更はコンパイルエラーとなる

要素の追加

要素の追加の方法は2つあります。
配列末尾に要素を追加するappend(_:)メソッドを利用する方法と任意の位置に要素を追加するinsert(_: at:)メソッドを利用する方法です。
なお、要素の変更と同様にこれら2つの追加操作も定数の配列に対して行うことはできません。

末尾への要素の追加

末尾に要素を追加する場合、Array<Element>型のappend(_:)メソッドを利用します。
append(_:)メソッドは引数に追加したい値を渡して利用します。

var stringArray = ["abc", "def", "ghi"]

// append(_:)メソッドによる要素の追加
stringArray.append("jkl")
print(stringArray) // ["abc", "def", "ghi", "jkl"]

任意の位置への要素の追加

任意のインデックスに要素を追加する場合、Array<Element>型のinsert(_: at:)メソッドを利用します。
insert(_: at:)メソッドは第一引数に追加したい値、第二引数に値を追加したい位置のインデックスを渡して利用します。

var stringArray = ["abc", "def", "ghi"]

// append(_:)メソッドによる要素の追加
stringArray.insert("jkl", at: 1)
print(stringArray) // ["abc", "jkl", "def", "ghi"]

要素の削除

要素の削除には3つの方法があります。
配列末尾の要素を削除するremoveLast()メソッド、配列の任意のインデックスの要素を削除するremove(at:)メソッド、配列の全ての要素を削除するremoveAll()メソッドの3通りの方法です。
なお、これら3つの削除操作も定数の配列に対して行うことはできません。

末尾の要素の削除

末尾の要素を削除する場合、Array<Element>型のremoveLast()メソッドを利用します。
removeLast()は引数を持たないメソッドなので、そのまま呼び出して利用します。

var stringArray = ["abc", "def", "ghi", "jkl"]

// removeLast()メソッドによる要素の削除
stringArray.removeLast()
print(stringArray) // ["abc", "def", "ghi"]

任意のインデックスの要素の削除

任意のインデックスの要素を削除する場合、Array<Element>型のremove(at:)メソッドを利用します。
remove(at:)メソッドは削除したい要素のインデックスを引数に渡して利用します。

var stringArray = ["abc", "def", "ghi", "jkl"]

// remove(at:)メソッドによる要素の削除
stringArray.remove(at: 2)
print(stringArray) // ["abc", "def", "jkl"]

全ての要素の削除

全ての要素を削除する場合、Array<Element>型のremoveAll()メソッドを利用します。
removeAll()メソッドは引数を持たないメソッドなので、そのまま呼び出して利用します。

var stringArray = ["abc", "def", "ghi", "jkl"]

// removeAll()メソッドによる要素の削除
stringArray.removeAll()
print(stringArray) // []

配列の結合

Array<Element>型は+演算子を利用して結合することができます。
“配列1 + 配列2″という書き方で配列を結合でき、何個でも連結可能です。

let stringArray1 = ["abc", "def", "ghi", "jkl"]
let stringArray2 = ["MNO", "PQR", "STU"]

// 配列の結合
// 値を変更するわけではなく、結合して新たな配列を作成するだけなので定数の配列も利用可能
let stringArray = stringArray1 + stringArray2
print(stringArray) // ["abc", "def", "ghi", "jkl", "MNO", "PQR", "STU"]

また、違う型の配列同士を結合する場合、Array<Any>型への代入時のみ可能となります。
それ以外の場合、コンパイルエラーとなります。

let stringArray = ["abc", "def", "ghi", "jkl"]
let intArray = [1, 2, 3]

// Array<Any>型の変数・定数への代入であれば違う型の配列同士の結合も可能
let anyArray: [Any] = stringArray + intArray // ["abc", "def", "ghi", "jkl", 1, 2, 3]
// 違う型の配列同士の結合は通常はコンパイルエラーとなる
print(stringArray + intArray) // コンパイルエラー

多次元配列

多次元配列とは

最後に多次元配列について説明します。
配列の定義の章の中で、Element型にはどのような型も定義可能と説明しました。
なので当然、Array<Element>型をElement型に定義することも可能となります。
配列の要素に配列を定義することで、2重、3重、4重… と配列は多重化していくことができるのです。
このように多重化した配列を多次元配列と言い、n重に多重化している配列をn次元配列と言います。
例えば、2重になっている配列は2次元配列、3重になっている配列は3次元配列といった形です。

多次元配列の利用

多次元配列はこの記事内で説明してきた配列の利用方法が理解できていれば問題なく利用できると思います。
今回は2次元配列で利用例をまとめます。

// 2次元配列の利用例
// 2次元配列定数定義
var twoDimensionalArray: [[Int]]
// 2次元配列のリテラル
twoDimensionalArray = [[1, 2, 3], [4, 5, 6], [7, 8]]
// 2次元配列の要素へアクセス
print(twoDimensionalArray[0]) // [1, 2, 3]
print(twoDimensionalArray[1]) // [4, 5, 6]
print(twoDimensionalArray[2]) // [7, 8]
// 要素内の配列を操作
twoDimensionalArray[2].append(9)
twoDimensionalArray[1][1] = 500
twoDimensionalArray[0].remove(at: 0)
print(twoDimensionalArray) // [[2, 3], [4, 500, 6], [7, 8, 9]]
// 2次元配列そのものを操作
twoDimensionalArray.insert([0, 1], at: 0)
print(twoDimensionalArray) // [[0, 1], [2, 3], [4, 500, 6], [7, 8, 9]]

アクセスした先の要素も配列になっているということを意識できれば問題なく操作できると思います。
今回は2次元配列を例にしていますが、さらに多次元の配列の場合も同じように、アクセスした先の要素が配列になっていることを意識すれば扱うことはできると思います。

とはいえ、行列を表すときなどに2次元配列が利用されることはよくありますが、それより多次元の配列は実際にはあまり使われません。
3次元以上に多重化した配列は複雑でわかりにくく、必要なシーンもほとんどないからです。
2次元配列までは利用できるようにし、それ以上は知識として知っておけば十分でしょう。

まとめ

今回はArray<Element>型についてまとめてきました。
あらゆるプログラミングにおいて重要な概念である配列を扱う型なので、意味や操作方法などをしっかりと認識した上で正しく利用していきましょう。
ここまでお読みいただきありがとうございました。
それでは、また。