コレクション型(Collection Types)
最終更新日: 2022/12/3
原文: https://docs.swift.org/swift-book/LanguageGuide/CollectionTypes.html
配列、セット、および辞書を使用してデータを編成する。
Swift は、配列、セット、辞書と呼ばれる 3 つの基本的な_コレクション型_を提供しています。配列は順序が決まったコレクションです。セットは値の重複と順序のないコレクションです。辞書はキーとバリューに関連性を持たせた順序のないコレクションです。

Collection Types(コレクション型)
Swift の配列、セット、辞書は、常に保持しているキーやバリューの型が明確です。つまり、間違った型の値を挿入することができません。これは、コレクションから取得する値の型がはっきりとわかっていることにもなります。
もし配列、セット、辞書を変数に代入した場合、作成されたコレクションは可変です。つまり、追加、削除、要素の変更など、後々そのコレクションを取り替え(または変更)できます。もし配列、セット、辞書を定数に代入した場合、コレクションは不変で、そのサイズや内容を変更できません。
NOTE コレクションの変更が必要ない場合は、不変なコレクションを作成するのが良いプラクティスです。そうすることで、コードを理解しやすくし、Swift のコンパイラもコレクションのパフォーマンスを最適化することができます。
配列は同じ型の値を順序立ったリストの中に保持します。配列の中に同じ値を複数回入れることができます。
NOTEArray
は Foundation のNSArray
とスムーズにやりとりできるようにしています。Foundation と Cocoa を使ったArray
の使用方法に関しては、Bridging Between Array and NSArrayを参照ください
Swift の配列の型は全体で
Array<Element>
と書きます。Element
はその配列が保持できる値の型です。簡略記法として [Element]
とも書けます。この 2 つの形式は機能的に同じですが、簡略記法の方が好まれ、このガイド内でも配列の型を参照する際はこちらの形式を使います。イニシャライザの構文を使用して、ある型の空の配列を作成できます。
var someInts: [Int] = []
print("someInts は \(someInts.count) 個の要素を持つ [Int] 型です。")
// someInts は 0 個の要素を持つ [Int] 型です。
someInts
変数の型は、イニシャライザから [Int]
と推論されます。他にも、コンテキスト的に型情報がわかっている場合(関数の引数や既に型が決まっている変数や定数など)は、空配列リテラルの
[]
(空の角括弧ペア)を使用して、空の配列を作成することができます。someInts.append(3)
// someInts Int 型の 3 を含んでいます
someInts = []
// someInts は空の配列だけど [Int] 型
Array
は同じデフォルト値を設定した特定にサイズの配列を作成するイニシャライザも提供しています。このイニシャライザに適切な型のデフォルト値(repeating
)と、その値の繰り返し回数(count
)を渡します。var threeDoubles = Array(repeating: 0.0, count: 3)
// threeDoubles は [Double] 型で、 [0.0, 0.0, 0.0] と等しい
加算演算子(
+
)を使用して、既存の型互換のある 2 つの配列を合成して、新しい配列を作成することもできます。この新しい配列の型は連結させた配列の型から推論されます:var anotherThreeDoubles = Array(repeating: 2.5, count: 3)
// anotherThreeDoubles は [Double] 型で [2.5, 2.5, 2.5] と等しい
var sixDoubles = threeDoubles + anotherThreeDoubles
// sixDoubles は [Double] と推論され、 [0.0, 0.0, 0.0, 2.5, 2.5, 2.5] と等しい
配列リテラルからも配列を初期化できます。これは、1 つ以上の要素を持った配列コレクションの簡略記法です。配列リテラルはカンマ区切りの角括弧(
[]
)で囲んだ値のリストです:
Array Literal
下記の例は、
String
を保持する shoppingList
という配列を作成しています。var shoppingList: [String] = ["Eggs", "Milk"]
// shoppingList は 2 つの 初期値で初期化されている
shoppingList
変数は、[String]
と書くことで「文字列を保持する配列」として宣言されています。この配列は値の型が String
で特定されているので、String
のみを保持することができます。ここで、shoppingList
配列は、配列リテラルの中で "Eggs"
と "Milk"
の 2 つの値で初期化しています。NOTE 後の例でさらにアイテムを追加するので、shoppingList
配列はlet
で宣言された定数ではなく、var
で変数として宣言されてます。
この例では、配列リテラルには 2 つの
String
を含んでいます。これは shoppingList
変数の宣言時の型(String
しか含めない)に合致しているので、shoppingList
に配列リテラルを代入して 2 つに初期値で初期化することができます。Swift の型推論のおかげで、同じ型の値を含んだ配列リテラルで初期化するときに配列の型を書く必要はありません。先ほどの
shoppingList
の例の初期化は、より簡潔な方法で書くことができました:var shoppingList = ["Eggs", "Milk"]
配列リテラルの全ての値は同じ型なので、Swift は
shoppingList
変数で使われている型は [String]
が適切だと推論できます。メソッドやプロパティ、サブスクリプト構文を通して配列の要素へのアクセス、変更ができます。
配列のアイテムの数を調べるために、読み取り専用の
count
プロパティをチェックします。print("ショッピングリストには \(shoppingList.count) 個のアイテムがあります。")
// ショッピングリストには 2 個のアイテムがあります。
Bool
型の isEmpty
プロパティは、count
プロパティが 0
かどうかをチェックする簡略記法です。if shoppingList.isEmpty {
print("ショッピングリストは空です。")
} else {
print("ショッピングリストは空ではありません。")
}
// ショッピングリストは空ではありません。
append(_:)
メソッドを使用して、配列の末尾に新しいアイテムを追加することができます。shoppingList.append("Flour")
// shoppingList は 3 つのアイテムを含んでいて、誰かがパンケーキを作っています
加算代入演算子(
+=
)を使用して 1 つ以上の互換性のある型のアイテムを追加することもできます。shoppingList += ["Baking Powder"]
// shoppingList 4 つのアイテムを含んでいます
shoppingList += ["Chocolate Spread", "Cheese", "Butter"]
// shoppingList は 7 つのアイテムを含んでいます
サブスクリプト構文を使用すると、配列から値を取得します。配列名のすぐ後の角括弧(
[]
)の中に、取得したい値のインデックスを渡します。var firstItem = shoppingList[0]
// firstItem は "Eggs" と等しい
NOTE 配列の最初のアイテムのindexは0
であって1
ではありません。SwiftのArrayはいつも 0 から始まるインデックス方式です。
あるインデックスの既存の値を変更したい場合もサブスクリプト構文を使います。
shoppingList[0] = "Six eggs"
// リストの最初のアイテムは "Eggs" ではなく、 "Six eggs"
サブスクリプト構文を使用するとき、指定したインデックスは値の存在する範囲内でなければなりません。例えば、配列の最後に値を追加しようとして
shoppingList[shoppingList.count] = "Salt"
と書くと、実行時エラーになります。ある一定範囲の値を一度に変更する場合にも、サブスクリプト構文を使用することができます。これは置き換えたい値のセットの数と指定した置き換える範囲の長さが異なっていても可能です。次の例は
"Chocolate Spread"
、"Cheese"
、"Butter"
を "Bananas"
、"Apples"
に置き換えています。shoppingList[4...6] = ["Bananas", "Apples"]
// shoppingList は 6 つのアイテムを含んでいます
配列の特定のインデックスにアイテムを挿入したい場合、
insert(_:at:)
メソッドを使います。shoppingList.insert("Maple Syrup", at: 0)
// shoppingList は 7 つのアイテムを含んでいます
// "Maple Syrup" は最初のアイテムです
この例では
insert(_:at:)
に "Maple Syrup"
とインデックス 0 を指定して、ショッピングリストの先頭に新しいアイテムを挿入しています。同様に
remove(at:)
を使用して配列からアイテムを削除できます。このメソッドは特定のインデックスのアイテムを削除し、削除したアイテムを返します(必要なければ戻り値は無視できます):let mapleSyrup = shoppingList.remove(at: 0)
// インデックス 0 にあったアイテムは削除されました
// shoppingList は 6 つのアイテムを含んでいますが、 Maple Syrupはありません
// mapleSyrup 定数は削除した "Maple Syrup" 文字列と等しい
NOTE 配列の既存の境界を超えたインデックスの値にアクセスしたり、変更したりしようとすると、実行時エラーになるでしょう。count
プロパティとインデックスを比較して、インデックスが妥当かどうかをチェックしてください。配列は 0 から始まるインデックス方式なので、妥当な最大のインデックスはcount - 1
です。しかし、count
が0
のとき(配列が空のとき)、妥当なインデックスは存在しません。
アイテムが削除された時、配列内の隙間は埋められ、インデックス
0
の値は再び "Six eggs"
になります。firstItem = shoppingList[0]
// firstItem は "Six eggs" と等しい
配列の最後の値を削除したい場合、
count
プロパティを探すコストを避けるためには、remove(at:)
よりも removeLast()
を使います。remove(at:)
と同様に removeLast()
も削除したアイテムを返します:let apples = shoppingList.removeLast()
// 最後のアイテムは削除されました
// shoppingList は 5 つのアイテムを含んでいますが、 apples はありません
// apples 定数は削除された "Apples" 文字列と等しい
for-in
ループを使用して配列の値全部に繰り返し処理をすることができます。for item in shoppingList {
print(item)
}
// Six eggs
// Milk
// Flour
// Baking Powder
// Bananas
各アイテムのインデックスが必要な場合は、
enumerated()
を代わりに使いましょう。enumerated()
は数値のインデックスとアイテムを組み合わせたタプルを返します。数値の開始は 0
で、1
ずつ増加していきます。つまり全体を繰り返し処理すると、数値はアイテムのインデックスと一致します。繰り返し処理の中で、インデックスとアイテムのタプルを一時的な定数や変数に展開することができます。for (index, value) in shoppingList.enumerated() {
print("アイテム \(index + 1): \(value)")
}
// アイテム 1: Six eggs
// アイテム 2: Milk
// アイテム 3: Flour
// アイテム 4: Baking Powder
// アイテム 5: Bananas
_セット_はコレクション内に、同じ型の値を、決まった順序 と値の重複なしに保持します。アイテムの順序が重要でない場合や、アイテムに重複がないことを保証したい場合に、配列の変わりにセットを使用することができます。
Set
は Foundation のNSSet
とスムーズにやりとりできるようにしています。Foundation と Cocoa を使ったSet
の使用方法に関しては、Bridging Between Set and NSSetを参照ください
セットに保存する型はハッシュ化が可能でなければなりません。つまり、その型はハッシュ値を計算する方法をセットに知らせる必要があります。ハッシュ値は、
Int
型で、等価比較が可能な全てのオブジェクトで、例えば a == b
の場合、a
のハッシュ値は b
のハッシュ値と等しくなります。Swift の基本的な型(
String
、Int
、Double
、Bool
など)は、デフォルトでハッシュ化が可能で、セットや辞書のキーに使用することができます。関連値を持たない列挙型のケース(Enumerations(列挙型))もデフォルトでハッシュ化が可能です。NOTE Swift 標準ライブラリのHashable
プロトコルに準拠することで、独自で作成した型をセットや辞書のキーに使用できます。hash(into:)
メソッドの実装については、Hashableを参照ください。プロトコルの準拠については、Protocols(プロトコル)を参照ください。
セット型は
Set<Element>
と書きます。Element
はセットが保持できる型です。セットには、配列のような簡略記法([Element]
)はありません。イニシャライザの構文を使用して、ある型の空のセットを作成できます。
var letters = Set<Character>()
print("letters は \(letters.count) 個の要素を持つ Set<Character> 型です。")
// letters は 0 個の要素を持つ Set<Character> 型です。
NOTEletters
変数の型はイニシャライザの型からSet<Character>
と推論されます。
他の方法として、関数の引数や型が明示されている変数や定数など型情報が既にわかっている場合は、空の配列リテラルを使用して空のセットを作成することができます。
letters.insert("a")
// letters は Character 型の値を 1 つ含んでいます
letters = []
// letters は空のセットですが、 型は Set<Character> のままです
簡略記法として、1 つ以上の値を配列リテラルを使用してセットを初期化することもできます。
下記の例は、
favoriteGenres
という String
の値を保持するセットを作成しています。var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
// favoriteGenres は 3 つ の初期値で初期化されている
favoriteGenres
変数は Set<String>
と書くことで、「String
のセット」を宣言しています。String
型の値を保持しているため、このセットには String
しか保持できません。ここでは favoriteGenres
セットに 3 つの String
を含めた配列リテラルを使用して初期化しています。NOTE 後の例でアイテムの追加や削除を行なっているため、favoriteGenres
は定数ではなく変数で定義されています。
セットの型は配列リテラルのみからは型推論することはできず、
Set
を明示しなければなりません。しかし、Swift の型推論によって、1 つの型しか持たない配列リテラルの場合は、要素の型を書かなくても初期化できます。favoriteGenres
の初期化は下記のようにより簡単に書くことができました:var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]
配列リテラルの値は全て同じ型なので、
favoriteGenres
変数は Set<String>
が正しい型だと推論できます。メソッドやプロパティを通してセットにアクセスしたり、変更できます。
セットのアイテムの数を調べるために、読み取り専用の
count
プロパティをチェックします。print("私には \(favoriteGenres.count) 個の好きな音楽ジャンルがあります。")
// 私には 3 個の好きな音楽ジャンルがあります。
Bool
型の isEmpty
プロパティは、count
プロパティが 0
かどうかをチェックする簡略記法です。if favoriteGenres.isEmpty {
print("音楽に関しては、こだわりはありません。")
} else {
print("私は音楽にこだわりがあります。")
}
// 私は音楽にこだわりがあります。
insert(_:)
メソッドを使用して、セットに新しいアイテムを追加することができます。favoriteGenres.insert("Jazz")
// favoriteGenres 4 つのアイテムを含んでいます
remove(_:)
を使用してセットからアイテムを削除できます。セットにアイテムが存在した場合は削除し、削除したアイテムを返します。もし存在しなけば nil
を返します。他の方法として、全アイテムを削除するには removeAll()
を使います。if let removedGenre = favoriteGenres.remove("Rock") {
print("\(removedGenre)? もういいです。")
} else {
print("そのジャンルはあんまり気にしたことがないです。")
}
// Rock? もういいです。
特定のアイテムが含まれているかどうかを調べるには、
contains(_:)
メソッドを使用することができます。if favoriteGenres.contains("Funk") {
print("James BrownのGet On The Good Footは最高です!")
} else {
print("ちょっとファンキー(funky)すぎます。")
}
// ちょっとファンキー(funky)すぎます。
for-in
ループを使用してセットの要素を繰り返し処理することができます。for genre in favoriteGenres {
print("\(genre)")
}
// Classical
// Jazz
// Hip hop
Swift の Set 型は決まった順序がありません。特定の順番で値を繰り返し処理したい場合、
sorted()
メソッドを使用すると、<
演算子を使用してソートされた配列として要素を返します。for genre in favoriteGenres.sorted() {
print("\(genre)")
}
// Classical
// Hip hop
// Jazz
2 つのセットの、合成、共通の要素の発見、値が全部等しいのか、いくつか等しいのか、全く違うのかの比較など、基本的なセットの操作を効率的に行うことができます。
下記のイラストでは、
a
と b
の 2 つのセットに対して、それぞれ操作を行い、色の付いた部分が結果(戻り値)を表しています。
Setのベン図
intersection(_:)
は、2 つのセットの共通要素を含めた新しいセットを作成しますsymmetricDifference(_:)
は、どちらかのセットにあるものの、両方には含まれていない要素を含めた新しいセットを作成しますunion(_:)
は、両方のセットに含まれている全ての要素を含めた新しいセットを作成しますsubtracting(_:)
は、特定のセットには含まれていない要素を含めた新しいセットを作成します
let oddDigits: Set = [1, 3, 5, 7, 9]
let evenDigits: Set = [0, 2, 4, 6, 8]
let singleDigitPrimeNumbers: Set = [2, 3, 5, 7]
oddDigits.union(evenDigits).sorted()
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
oddDigits.intersection(evenDigits).sorted()
// []
oddDigits.subtracting(singleDigitPrimeNumbers).sorted()
// [1, 9]
oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted()
// [1, 2, 9]