Sponsor Link
環境&対象
- macOS Ventura 13.4
- Xcode 14.3
- iOS 16.4 beta
Dictionary
Dictionary 型は、キー~バリュー のペアを保持することのできる型です。
Apple のドキュメントは、こちら。
キーは、Hashable であることが必要です。バリュー には特に制約はありません。
キーは、Dictionary の中でユニークであることが必要で、基本的に Hash が異なることが必要です。
基本的にと書いたのは、Hash を使って効率よく検索できるようにしているためです。Hash が異なることは絶対条件ではありませんが、重複が複数発生することは Hash の計算方法が適切でない可能性があります。
Syntax Sugar
キー型が Int、バリュー型が String であるような Dictionary は、以下のように定義します。
let myDic: Dictionary<Int,String> = ....
Syntax Sugar を使って、以下のように定義することもできるようになっています。
let myDic: [Int: String] = ...
角括弧([と])をつかった [型] という書き方は、Array や Set を意味していましたが、あいだにコロン(:) を入れた [型:型] という書き方は、辞書を意味します。
型宣言だけでなく、辞書の中身の定義にも キー:バリュー という書き方を使用します。
let myDic: [Int: String] = [ 1: "One", 2: "Two", 3: "Three"]
Swift での他の宣言文と同じように、文から型が推測できる時には、変数の型定義を省略することも可能です。
let myDic = [ 1: "One", 2: "Two", 3: "Three"]
nil を バリューに持つ Dictionary
使用シーンによっては、Dictionary の バリューに nil を持たせたいことがあります。
例えば、バリュー型に nil を取り得る Int? 型のように Optional 型を使用したいというケースです。
# キーは、Hashable であることが必要なので、Optional になれません。
定義としては、次のように定義すれば良いはずです。
var myDic: [Int: Int?] = [:]
コンパイルも上手くできますし問題ありません。
ところが、nil を設定しようとすると、上手く動きません。
ふつう(?)に考えると、そのままセットすれば良さそうですが、期待と異なる結果になります。
var myDic: [Int: Int?] = [:]
myDic[2] = nil
print(myDic) // print-out: [:]
nil をセットしているつもりですが、何も保持していない 辞書のままです。
次のようなコードを実行すると何が起きているかわかりやすくなります。
var myDic: [Int: Int?] = [:]
myDic[2] = 3
print(myDic) // print-out: [2: Optional(3)]
myDic[2] = nil
print(myDic) // print-out: [:]
nil をセットすると、そのキー値に対する バリューを削除しているように見えます。
実は Dictionary のドキュメントを読むと書いてあるのですが、キー値に対して nil をセットすると、辞書から そのキー値を削除する動作をします。つまり、以下の2つのコードは同じ意味を持つコードです。
// キー値: 2 にnil を設定する
myDic[2] = nil
// キー値: 2 を辞書から削除する
myDic.removeValue(forKey: 2)
Dictionary の Value に nil をセットする方法
では、バリューに Optional を設定しても、nil は設定できないのか? というと以下のようにすることで設定できるようになります。
var myDic: [Int: Int?] = [:]
myDic[2] = Optional<Int>.none
print(myDic) // print-out: [2: nil]
これとよく似た使い方が SwiftUI での tag 指定でも有効です。
[SwiftUI] Picker/List と tag 指定 (nil 設定の方法)
キー値の Hashable が同じ値を持つとどうなる?
キーの型となるためには、Hashable であることが必要ですが、その Hashable であることを少し見てみます。
以下のような 型を キーとして使うことを考えます。
struct BigValue: Hashable {
var value: Int
init(_ value: Int) {
self.value = value
}
func hash(into hasher: inout Hasher) {
let smallValue = value / 100
hasher.combine(smallValue)
}
}
保持している Int の下2桁を除いた数値を ハッシュ値として使用する型です。
下2桁以外が同じ数字 例えば 1000 ~ 1099 は、同じハッシュ値を持ちます。
struct BigValue: Hashable {
var value: Int
init(_ value: Int) {
self.value = value
}
func hash(into hasher: inout Hasher) {
let smallValue = value / 100
hasher.combine(smallValue)
}
}
print(BigValue(1000).hashValue) // print-out: 1579821705450413174
print(BigValue(1090).hashValue) // print-out: 1579821705450413174
上記は、Playground を使って、1000 と 1090 で同じハッシュ値を持つことを確認したものです。
この BigValue という型をキーとしてもつ Dictionary を考えます。
var dic: [BigValue: Int] = [:]
dic[BigValue(1000)] = 3
dic[BigValue(1090)] = 4
print(dic[BigValue(1000)]) // print-out: Optional(3)
print(dic[BigValue(1090)]) // print-out: Optional(4)
上記を見るとわかりますが、ハッシュ値が同じになるキーに対しても バリューはそれぞれ保持してくれています。
ただし、ハッシュ値が同じであるということは計算が早いであろう ハッシュだけで特定することができず値の取得が遅くなることが予想されます。(実測はしていません)
通常、重なりやすいハッシュ計算は、良い実装とは考えられません。
まとめ
Dictionary の使い方 Optional Value / Hashable の確認
- Dictionary<Key型、Value型>と定義する
- [Key型: Value型] とも定義できる
- Key型 は Hashable であることが必要
- Key型 の ハッシュ値が重複しても Value は保持してくれる
- Key型 は Optional 型にはできない
- Value型 は Optional 型にできる
- Value型 が Optional 型の時で nil を設定する時は .none を使うことが必要
説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。
SwiftUI おすすめ本
SwiftUI を理解するには、以下の本がおすすめです。
SwiftUI ViewMatery
SwiftUI で開発していくときに、ViewやLayoutのための適切なmodifierを探すのが大変です。
英語での説明になってしまいますが、以下の”SwiftUI Views Mastery Bundle”という本がビジュアル的に確認して探せるので、便利です。
英語ではありますが、1ページに コードと画面が並んでいるので、非常にわかりやすいです。
View に適用できる modifier もわかりやすく説明されているので、ビューの理解だけではなく、どのような装飾ができるかも簡単にわかります。
超便利です
販売元のページは、こちらです。
SwiftUI 徹底入門
# SwiftUI は、毎年大きく改善されていますので、少し古くなってしまいましたが、いまでも 定番本です。
Swift学習におすすめの本
詳解Swift
Swift の学習には、詳解 Swift という書籍が、おすすめです。
著者は、Swift の初期から書籍を出していますし、Swift の前に主力言語だった Objective-C という言語についても同様の書籍を出しています。
最新版を購入するのがおすすめです。
Sponsor Link