Dictionary是除了Array之外的另一種非常重要的數(shù)據(jù)結(jié)構(gòu)赞庶,它用于把某種形式的key集绰,關(guān)聯(lián)到某種形式的value菠劝。我們來(lái)看一個(gè)例子赛糟。
定義Dictionary
假設(shè)我們要定義一個(gè)數(shù)據(jù)結(jié)構(gòu),用來(lái)保存用戶在泊學(xué)對(duì)某個(gè)視頻的觀看情況摸恍∠ず保可以這樣:
enum RecordType {
case bool(Bool)
case number(Int)
case text(String)
}
let record11: [String: RecordType] = [
"uid": .number(11),
"exp": .number(100),
"favourite": .bool(true),
"title": .text("Dictionary basics")
]
在上面代碼里,我們用[KeyType: ValueType]的形式來(lái)定義一個(gè)Dictionary立镶。當(dāng)定義好Dictionary之后壁袄,我們就能直接用[Key]來(lái)訪問(wèn)某個(gè)key對(duì)應(yīng)的值了:
record11["uid"] // number(11)
record11["favourite"] // bool(true)
record11["title"] // text("Dictionary basics")
record11["invalid"] // nil
// Optional<RecordType>.Type
type(of: record11["favourite"])
上面例子中的結(jié)果都很直觀。但是有一個(gè)細(xì)節(jié)卻是值得我們注意的媚媒。和Array不同的是嗜逻,[]用在Dictionary的時(shí)候,會(huì)返回一個(gè)Optional類(lèi)型來(lái)確保這種形式的訪問(wèn)安全缭召。因此栈顷,訪問(wèn)不存在的key,并不會(huì)導(dǎo)致運(yùn)行時(shí)錯(cuò)誤恼琼。
你怎么理解這種差異呢妨蛹?
這是因?yàn)樗饕@個(gè)概念屏富,對(duì)Array和Dictionary來(lái)說(shuō)晴竞,是截然不同的。對(duì)于Array來(lái)說(shuō)狠半,我們有可能使用的正常索引值只源于Array自身噩死,也就是0..<array.count,因此神年,如果你使用了不在這個(gè)范圍里的值已维,則一定是可以被定性為Bug的,何況已日,我們之前也看到了垛耳,對(duì)于Array,我們幾乎不需要直接使用索引來(lái)訪問(wèn)元素飘千。
而對(duì)于Dictionary來(lái)說(shuō)堂鲜,它包含的內(nèi)容并不直接決定我們可以查詢的內(nèi)容。舉個(gè)例子來(lái)說(shuō)护奈,英漢詞典中也可能并不包含我們要查詢的單詞缔莲。所以,Dictionary中包含的所有鍵值霉旗,從語(yǔ)義上說(shuō)痴奏,并不完全決定了它的使用者會(huì)查詢的值蛀骇,所以,我們也無(wú)法把這類(lèi)問(wèn)題明確的歸因于是Bug读拆。所以擅憔,Swfit為Dictionary的索引查詢操作,提供了optional保護(hù)建椰。要么得到正確的結(jié)果雕欺,要么通過(guò)nil表示要查詢的內(nèi)容不存在。
常用的基本屬性
作為一個(gè)集合類(lèi)型棉姐,Dictionary同樣有count和isEmpty兩個(gè)屬性讀取其元素的個(gè)數(shù)以及判斷其是否為空:
record11.count // 4
record11.isEmpty // false
另外屠列,我們可以單獨(dú)訪問(wèn)一個(gè)Dictionary的所有keys和所有values:
record11.keys
record11.values
這兩個(gè)屬性也分別是一個(gè)集合,我們可以暫時(shí)忽略掉它們具體的類(lèi)型伞矩,如果要我們要訪問(wèn)它們的每一個(gè)元素笛洛,直接用for循環(huán)或forEach遍歷就好了:
for key in record11.keys { print(key) }
// or
record11.keys.forEach { print($0) }
添加、更新和刪除元素
和Array一樣乃坤,Dictionary也是一個(gè)值類(lèi)型苛让,當(dāng)我們復(fù)制Dictionary對(duì)象的時(shí)候,就會(huì)拷貝Dictionary中的所有內(nèi)容:
var record10 = record11
并且湿诊,直接使用key就可以訪問(wèn)和修改Dictionary的內(nèi)容:
record10['favourite'] = .bool(false) // false
record11['favourite'] // true
如果我們希望更新value的時(shí)候狱杰,同時(shí)獲得修改前的值,還可以使用updateValue(_:forKey:)方法:
record10.updateValue(.bool(true),
forKey: "favourite") // .bool(false)
從上面的結(jié)果可以看出修改record10并不會(huì)影響record11厅须。
當(dāng)我們要在Dictionary中添加元素時(shí)仿畸,直接給要添加的key賦值就好了:
record10["watchLater"] = .bool(false)
// [
// "favourite": RecordType.bool(false),
// "exp": RecordType.number(100),
// "title": RecordType.text("Directory basics"),
// "uid": RecordType.number(11),
// "watchLater": RecordType.bool(false)
// ]
這樣,record10中的內(nèi)容朗和,就變成了5項(xiàng)错沽。而當(dāng)我們要?jiǎng)h除特定的key時(shí),直接把它的值設(shè)置為nil:
record10["watchLater"] = nil
// [
// "favourite": RecordType.bool(false),
// "exp": RecordType.number(100),
// "title": RecordType.text("Directory basics"),
// "uid": RecordType.number(11)
// ]
這里眶拉,并不是把特定key的值設(shè)置為nil(畢竟Dictionary中value部分的類(lèi)型也不是optional)千埃,而是刪除特定的key。當(dāng)某個(gè)key的value被設(shè)置成nil后忆植,這個(gè)key也就從Dictionary中刪除了放可。
遍歷Dictionary
由于Dictionary同時(shí)包含了key和value,因此朝刊,我們也有多重方式來(lái)遍歷Dictionary耀里。最簡(jiǎn)單的,就是遍歷Dictionary中的每一個(gè)元素:
for (k, v) in record10 {
print("\(k): \(v)")
}
record10.forEach { print("\($0): \($1)") }
從上面的例子可以看到坞古,遍歷Dictionary和遍歷Array是類(lèi)似的备韧。當(dāng)我們使用for循環(huán)遍歷時(shí),它的每一個(gè)元素都用一個(gè)tuple來(lái)表示痪枫,封裝了每一個(gè)元素的key和value织堂。而當(dāng)使用forEach方法時(shí)叠艳,它會(huì)給它的closure參數(shù)傳遞兩個(gè)值,分別是每一個(gè)元素的key和value易阳。
但是附较,由于Dictionary是一個(gè)無(wú)序集合(unordered collection),因此當(dāng)我們編輯了Dictionary之后潦俺,每次遍歷拒课,訪問(wèn)元素的順序都可能是不同的。如果我們希望按照固定的順序來(lái)訪問(wèn)Dictionary中的元素事示,一個(gè)最簡(jiǎn)單的辦法早像,就是對(duì)key排序后,再進(jìn)行遍歷:
for key in record10.keys.sorted() {
print("\(key): \(record10[key])")
}
What's next?
在了解了Dictionary的基本用法之后肖爵,下一節(jié)卢鹦,我們通過(guò)extension給Dictionary添加一些標(biāo)準(zhǔn)庫(kù)中沒(méi)有但卻常用的操作,以此進(jìn)一步理解Dictionary的用法劝堪。