Swift傻傻分不清楚系列(六)集合类型
本頁(yè)包含內(nèi)容:
- 集合的可變性(Mutability of Collections)
- 數(shù)組(Arrays)
- 集合(Sets)
- 字典(Dictionaries)
Swift 語(yǔ)言提供Arrays、Sets和Dictionaries三種基本的集合類(lèi)型用來(lái)存儲(chǔ)集合數(shù)據(jù)。數(shù)組(Arrays)是有序數(shù)據(jù)的集。集合(Sets)是無(wú)序無(wú)重復(fù)數(shù)據(jù)的集。字典(Dictionaries)是無(wú)序的鍵值對(duì)的集。
Swift 語(yǔ)言中的Arrays、Sets和Dictionaries中存儲(chǔ)的數(shù)據(jù)值類(lèi)型必須明確。這意味著我們不能把不正確的數(shù)據(jù)類(lèi)型插入其中。同時(shí)這也說(shuō)明我們完全可以對(duì)取回值的類(lèi)型非常自信。
注意:
Swift 的Arrays、Sets和Dictionaries類(lèi)型被實(shí)現(xiàn)為泛型集合。
如果創(chuàng)建一個(gè)Arrays、Sets或Dictionaries并且把它分配成一個(gè)變量,這個(gè)集合將會(huì)是可變的。這意味著我們可以在創(chuàng)建之后添加更多或移除已存在的數(shù)據(jù)項(xiàng),或者改變集合中的數(shù)據(jù)項(xiàng)。如果我們把Arrays、Sets或Dictionaries分配成常量,那么它就是不可變的,它的大小和內(nèi)容都不能被改變。
注意:
在我們不需要改變集合的時(shí)候創(chuàng)建不可變集合是很好的實(shí)踐。如此 Swift 編譯器可以優(yōu)化我們創(chuàng)建的集合。
數(shù)組(Arrays)
數(shù)組使用有序列表存儲(chǔ)同一類(lèi)型的多個(gè)值。相同的值可以多次出現(xiàn)在一個(gè)數(shù)組的不同位置中。
注意: Swift 的Array類(lèi)型被橋接到Foundation中的NSArray類(lèi)。
集合類(lèi)型的哈希值
一個(gè)類(lèi)型為了存儲(chǔ)在集合中,該類(lèi)型必須是可哈希化的--也就是說(shuō),該類(lèi)型必須提供一個(gè)方法來(lái)計(jì)算它的哈希值。一個(gè)哈希值是Int類(lèi)型的,相等的對(duì)象哈希值必須相同,比如a==b,因此必須a.hashValue == b.hashValue。
Swift 的所有基本類(lèi)型(比如String,Int,Double和Bool)默認(rèn)都是可哈希化的,可以作為集合的值的類(lèi)型或者字典的鍵的類(lèi)型。沒(méi)有關(guān)聯(lián)值的枚舉成員值默認(rèn)也是可哈希化的。
注意:
你可以使用你自定義的類(lèi)型作為集合的值的類(lèi)型或者是字典的鍵的類(lèi)型,但你需要使你的自定義類(lèi)型符合 Swift 標(biāo)準(zhǔn)庫(kù)中的Hashable協(xié)議。符合Hashable協(xié)議的類(lèi)型需要提供一個(gè)類(lèi)型為Int的可讀屬性hashValue。由類(lèi)型的hashValue屬性返回的值不需要在同一程序的不同執(zhí)行周期或者不同程序之間保持相同。
因?yàn)镠ashable協(xié)議符合Equatable協(xié)議,所以符合該協(xié)議的類(lèi)型也必須提供一個(gè)"是否相等"運(yùn)算符(==)的實(shí)現(xiàn)。這個(gè)Equatable協(xié)議要求任何符合==實(shí)現(xiàn)的實(shí)例間都是一種相等的關(guān)系。也就是說(shuō),對(duì)于a,b,c三個(gè)值來(lái)說(shuō),==的實(shí)現(xiàn)必須滿足下面三種情況:
- a == a(自反性)
- a == b意味著b == a(對(duì)稱性)
- a == b && b == c意味著a == c(傳遞性)
集合類(lèi)型語(yǔ)法
Swift 中的Set類(lèi)型被寫(xiě)為Set<Element>,這里的Element表示Set中允許存儲(chǔ)的類(lèi)型,和數(shù)組不同的是,集合沒(méi)有等價(jià)的簡(jiǎn)化形式。
創(chuàng)建和構(gòu)造一個(gè)空的集合
你可以通過(guò)構(gòu)造器語(yǔ)法創(chuàng)建一個(gè)特定類(lèi)型的空集合:
var letters = Set<Character>() print("letters is of type Set<Character> with \(letters.count) items.") // 打印 "letters is of type Set<Character> with 0 items." 注意:
通過(guò)構(gòu)造器,這里的letters變量的類(lèi)型被推斷為Set<Character>。
此外,如果上下文提供了類(lèi)型信息,比如作為函數(shù)的參數(shù)或者已知類(lèi)型的變量或常量,我們可以通過(guò)一個(gè)空的數(shù)組字面量創(chuàng)建一個(gè)空的Set:
letters.insert("a") // letters 現(xiàn)在含有1個(gè) Character 類(lèi)型的值 letters = [] // letters 現(xiàn)在是一個(gè)空的 Set, 但是它依然是 Set<Character> 類(lèi)型
用數(shù)組字面量創(chuàng)建集合
你可以使用數(shù)組字面量來(lái)構(gòu)造集合,并且可以使用簡(jiǎn)化形式寫(xiě)一個(gè)或者多個(gè)值作為集合元素。
下面的例子創(chuàng)建一個(gè)稱之為favoriteGenres的集合來(lái)存儲(chǔ)String類(lèi)型的值:
var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"] // favoriteGenres 被構(gòu)造成含有三個(gè)初始值的集合這個(gè)favoriteGenres變量被聲明為“一個(gè)String值的集合”,寫(xiě)為Set<String>。由于這個(gè)特定的集合含有指定String類(lèi)型的值,所以它只允許存儲(chǔ)String類(lèi)型值。這里的favoriteGenres變量有三個(gè)String類(lèi)型的初始值("Rock","Classical"和"Hip hop"),并以數(shù)組字面量的方式出現(xiàn)。
注意:
favoriteGenres被聲明為一個(gè)變量(擁有var標(biāo)示符)而不是一個(gè)常量(擁有l(wèi)et標(biāo)示符),因?yàn)樗锩娴脑貙?huì)在下面的例子中被增加或者移除。
一個(gè)Set類(lèi)型不能從數(shù)組字面量中被單獨(dú)推斷出來(lái),因此Set類(lèi)型必須顯式聲明。然而,由于 Swift 的類(lèi)型推斷功能,如果你想使用一個(gè)數(shù)組字面量構(gòu)造一個(gè)Set并且該數(shù)組字面量中的所有元素類(lèi)型相同,那么你無(wú)須寫(xiě)出Set的具體類(lèi)型。favoriteGenres的構(gòu)造形式可以采用簡(jiǎn)化的方式代替:
var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]由于數(shù)組字面量中的所有元素類(lèi)型相同,Swift 可以推斷出Set<String>作為favoriteGenres變量的正確類(lèi)型。
訪問(wèn)和修改一個(gè)集合
你可以通過(guò)Set的屬性和方法來(lái)訪問(wèn)和修改一個(gè)Set。
為了找出一個(gè)Set中元素的數(shù)量,可以使用其只讀屬性count:
print("I have \(favoriteGenres.count) favorite music genres.") // 打印 "I have 3 favorite music genres."使用布爾屬性isEmpty作為一個(gè)縮寫(xiě)形式去檢查count屬性是否為0:
if favoriteGenres.isEmpty {print("As far as music goes, I'm not picky.") } else {print("I have particular music preferences.") } // 打印 "I have particular music preferences."你可以通過(guò)調(diào)用Set的insert(_:)方法來(lái)添加一個(gè)新元素:
favoriteGenres.insert("Jazz") // favoriteGenres 現(xiàn)在包含4個(gè)元素你可以通過(guò)調(diào)用Set的remove(_:)方法去刪除一個(gè)元素,如果該值是該Set的一個(gè)元素則刪除該元素并且返回被刪除的元素值,否則如果該Set不包含該值,則返回nil。另外,Set中的所有元素可以通過(guò)它的removeAll()方法刪除。
if let removedGenre = favoriteGenres.remove("Rock") {print("\(removedGenre)? I'm over it.") } else {print("I never much cared for that.") } // 打印 "Rock? I'm over it."使用contains(_:)方法去檢查Set中是否包含一個(gè)特定的值:
if favoriteGenres.contains("Funk") {print("I get up on the good foot.") } else {print("It's too funky in here.") } // 打印 "It's too funky in here."
遍歷一個(gè)集合
你可以在一個(gè)for-in循環(huán)中遍歷一個(gè)Set中的所有值。
for genre in favoriteGenres {print("\(genre)") } // Classical // Jazz // Hip hop更多關(guān)于for-in循環(huán)的信息,參見(jiàn)For 循環(huán)。
Swift 的Set類(lèi)型沒(méi)有確定的順序,為了按照特定順序來(lái)遍歷一個(gè)Set中的值可以使用sort()方法,它將返回一個(gè)有序數(shù)組,這個(gè)數(shù)組的元素排列順序由操作符'<'對(duì)元素進(jìn)行比較的結(jié)果來(lái)確定.
for genre in favoriteGenres.sort() {print("\(genre)") } // prints "Classical" // prints "Hip hop" // prints "Jazz
集合操作
你可以高效地完成Set的一些基本操作,比如把兩個(gè)集合組合到一起,判斷兩個(gè)集合共有元素,或者判斷兩個(gè)集合是否全包含,部分包含或者不相交。
基本集合操作
下面的插圖描述了兩個(gè)集合-a和b-以及通過(guò)陰影部分的區(qū)域顯示集合各種操作的結(jié)果。
- 使用intersect(_:)方法根據(jù)兩個(gè)集合中都包含的值創(chuàng)建的一個(gè)新的集合。
- 使用exclusiveOr(_:)方法根據(jù)在一個(gè)集合中但不在兩個(gè)集合中的值創(chuàng)建一個(gè)新的集合。
- 使用union(_:)方法根據(jù)兩個(gè)集合的值創(chuàng)建一個(gè)新的集合。
- 使用subtract(_:)方法根據(jù)不在該集合中的值創(chuàng)建一個(gè)新的集合。
集合成員關(guān)系和相等
下面的插圖描述了三個(gè)集合-a,b和c,以及通過(guò)重疊區(qū)域表述集合間共享的元素。集合a是集合b的父集合,因?yàn)閍包含了b中所有的元素,相反的,集合b是集合a的子集合,因?yàn)閷儆赽的元素也被a包含。集合b和集合c彼此不關(guān)聯(lián),因?yàn)樗鼈冎g沒(méi)有共同的元素。
- 使用“是否相等”運(yùn)算符(==)來(lái)判斷兩個(gè)集合是否包含全部相同的值。
- 使用isSubsetOf(_:)方法來(lái)判斷一個(gè)集合中的值是否也被包含在另外一個(gè)集合中。
- 使用isSupersetOf(_:)方法來(lái)判斷一個(gè)集合中包含另一個(gè)集合中所有的值。
- 使用isStrictSubsetOf(_:)或者isStrictSupersetOf(_:)方法來(lái)判斷一個(gè)集合是否是另外一個(gè)集合的子集合或者父集合并且兩個(gè)集合并不相等。
- 使用isDisjointWith(_:)方法來(lái)判斷兩個(gè)集合是否不含有相同的值(是否沒(méi)有交集)。
字典
字典是一種存儲(chǔ)多個(gè)相同類(lèi)型的值的容器。每個(gè)值(value)都關(guān)聯(lián)唯一的鍵(key),鍵作為字典中的這個(gè)值數(shù)據(jù)的標(biāo)識(shí)符。和數(shù)組中的數(shù)據(jù)項(xiàng)不同,字典中的數(shù)據(jù)項(xiàng)并沒(méi)有具體順序。我們?cè)谛枰ㄟ^(guò)標(biāo)識(shí)符(鍵)訪問(wèn)數(shù)據(jù)的時(shí)候使用字典,這種方法很大程度上和我們?cè)诂F(xiàn)實(shí)世界中使用字典查字義的方法一樣。
注意:
Swift 的Dictionary類(lèi)型被橋接到Foundation的NSDictionary類(lèi)。
字典類(lèi)型快捷語(yǔ)法
Swift 的字典使用Dictionary<Key, Value>定義,其中Key是字典中鍵的數(shù)據(jù)類(lèi)型,Value是字典中對(duì)應(yīng)于這些鍵所存儲(chǔ)值的數(shù)據(jù)類(lèi)型。
注意:
一個(gè)字典的Key類(lèi)型必須遵循Hashable協(xié)議,就像Set的值類(lèi)型。
我們也可以用[Key: Value]這樣快捷的形式去創(chuàng)建一個(gè)字典類(lèi)型。雖然這兩種形式功能上相同,但是后者是首選,并且這本指導(dǎo)書(shū)涉及到字典類(lèi)型時(shí)通篇采用后者。
創(chuàng)建一個(gè)空字典
我們可以像數(shù)組一樣使用構(gòu)造語(yǔ)法創(chuàng)建一個(gè)擁有確定類(lèi)型的空字典:
var namesOfIntegers = [Int: String]() // namesOfIntegers 是一個(gè)空的 [Int: String] 字典這個(gè)例子創(chuàng)建了一個(gè)[Int: String]類(lèi)型的空字典來(lái)儲(chǔ)存整數(shù)的英語(yǔ)命名。它的鍵是Int型,值是String型。
如果上下文已經(jīng)提供了類(lèi)型信息,我們可以使用空字典字面量來(lái)創(chuàng)建一個(gè)空字典,記作[:](中括號(hào)中放一個(gè)冒號(hào)):
namesOfIntegers[16] = "sixteen" // namesOfIntegers 現(xiàn)在包含一個(gè)鍵值對(duì) namesOfIntegers = [:] // namesOfIntegers 又成為了一個(gè) [Int: String] 類(lèi)型的空字典用字典字面量創(chuàng)建字典
我們可以使用字典字面量來(lái)構(gòu)造字典,這和我們剛才介紹過(guò)的數(shù)組字面量擁有相似語(yǔ)法。字典字面量是一種將一個(gè)或多個(gè)鍵值對(duì)寫(xiě)作Dictionary集合的快捷途徑。
一個(gè)鍵值對(duì)是一個(gè)key和一個(gè)value的結(jié)合體。在字典字面量中,每一個(gè)鍵值對(duì)的鍵和值都由冒號(hào)分割。這些鍵值對(duì)構(gòu)成一個(gè)列表,其中這些鍵值對(duì)由方括號(hào)包含、由逗號(hào)分割:
[key 1: value 1, key 2: value 2, key 3: value 3]下面的例子創(chuàng)建了一個(gè)存儲(chǔ)國(guó)際機(jī)場(chǎng)名稱的字典。在這個(gè)字典中鍵是三個(gè)字母的國(guó)際航空運(yùn)輸相關(guān)代碼,值是機(jī)場(chǎng)名稱:
var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]airports字典被聲明為一種[String: String]類(lèi)型,這意味著這個(gè)字典的鍵和值都是String類(lèi)型。
注意:
airports字典被聲明為變量(用var關(guān)鍵字)而不是常量(let關(guān)鍵字)因?yàn)楹髞?lái)更多的機(jī)場(chǎng)信息會(huì)被添加到這個(gè)示例字典中。
airports字典使用字典字面量初始化,包含兩個(gè)鍵值對(duì)。第一對(duì)的鍵是YYZ,值是Toronto Pearson。第二對(duì)的鍵是DUB,值是Dublin。
這個(gè)字典語(yǔ)句包含了兩個(gè)String: String類(lèi)型的鍵值對(duì)。它們對(duì)應(yīng)airports變量聲明的類(lèi)型(一個(gè)只有String鍵和String值的字典)所以這個(gè)字典字面量的任務(wù)是構(gòu)造擁有兩個(gè)初始數(shù)據(jù)項(xiàng)的airport字典。
和數(shù)組一樣,我們?cè)谟米值渥置媪繕?gòu)造字典時(shí),如果它的鍵和值都有各自一致的類(lèi)型,那么就不必寫(xiě)出字典的類(lèi)型。airports字典也可以用這種簡(jiǎn)短方式定義:
var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]因?yàn)檫@個(gè)語(yǔ)句中所有的鍵和值都各自擁有相同的數(shù)據(jù)類(lèi)型,Swift 可以推斷出Dictionary<String, String>是airports字典的正確類(lèi)型。
訪問(wèn)和修改字典
我們可以通過(guò)字典的方法和屬性來(lái)訪問(wèn)和修改字典,或者通過(guò)使用下標(biāo)語(yǔ)法。
和數(shù)組一樣,我們可以通過(guò)字典的只讀屬性count來(lái)獲取某個(gè)字典的數(shù)據(jù)項(xiàng)數(shù)量:
print("The dictionary of airports contains \(airports.count) items.") // 打印 "The dictionary of airports contains 2 items."(這個(gè)字典有兩個(gè)數(shù)據(jù)項(xiàng))使用布爾屬性isEmpty來(lái)快捷地檢查字典的count屬性是否等于0:
if airports.isEmpty {print("The airports dictionary is empty.") } else {print("The airports dictionary is not empty.") } // 打印 "The airports dictionary is not empty."我們也可以在字典中使用下標(biāo)語(yǔ)法來(lái)添加新的數(shù)據(jù)項(xiàng)。可以使用一個(gè)恰當(dāng)類(lèi)型的鍵作為下標(biāo)索引,并且分配恰當(dāng)類(lèi)型的新值:
airports["LHR"] = "London" // airports 字典現(xiàn)在有三個(gè)數(shù)據(jù)項(xiàng)我們也可以使用下標(biāo)語(yǔ)法來(lái)改變特定鍵對(duì)應(yīng)的值:
airports["LHR"] = "London Heathrow" // "LHR"對(duì)應(yīng)的值 被改為 "London Heathrow作為另一種下標(biāo)方法,字典的updateValue(_:forKey:)方法可以設(shè)置或者更新特定鍵對(duì)應(yīng)的值。就像上面所示的下標(biāo)示例,updateValue(_:forKey:)方法在這個(gè)鍵不存在對(duì)應(yīng)值的時(shí)候會(huì)設(shè)置新值或者在存在時(shí)更新已存在的值。和上面的下標(biāo)方法不同的,updateValue(_:forKey:)這個(gè)方法返回更新值之前的原值。這樣使得我們可以檢查更新是否成功。
updateValue(_:forKey:)方法會(huì)返回對(duì)應(yīng)值的類(lèi)型的可選值。舉例來(lái)說(shuō):對(duì)于存儲(chǔ)String值的字典,這個(gè)函數(shù)會(huì)返回一個(gè)String?或者“可選?String”類(lèi)型的值。
如果有值存在于更新前,則這個(gè)可選值包含了舊值,否則它將會(huì)是nil。
if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") {print("The old value for DUB was \(oldValue).") } // 輸出 "The old value for DUB was Dublin."我們也可以使用下標(biāo)語(yǔ)法來(lái)在字典中檢索特定鍵對(duì)應(yīng)的值。因?yàn)橛锌赡苷?qǐng)求的鍵沒(méi)有對(duì)應(yīng)的值存在,字典的下標(biāo)訪問(wèn)會(huì)返回對(duì)應(yīng)值的類(lèi)型的可選值。如果這個(gè)字典包含請(qǐng)求鍵所對(duì)應(yīng)的值,下標(biāo)會(huì)返回一個(gè)包含這個(gè)存在值的可選值,否則將返回nil:
if let airportName = airports["DUB"] {print("The name of the airport is \(airportName).") } else {print("That airport is not in the airports dictionary.") } // 打印 "The name of the airport is Dublin Airport."我們還可以使用下標(biāo)語(yǔ)法來(lái)通過(guò)給某個(gè)鍵的對(duì)應(yīng)值賦值為nil來(lái)從字典里移除一個(gè)鍵值對(duì):
airports["APL"] = "Apple Internation" // "Apple Internation" 不是真的 APL 機(jī)場(chǎng), 刪除它 airports["APL"] = nil // APL 現(xiàn)在被移除了此外,removeValueForKey(_:)方法也可以用來(lái)在字典中移除鍵值對(duì)。這個(gè)方法在鍵值對(duì)存在的情況下會(huì)移除該鍵值對(duì)并且返回被移除的值或者在沒(méi)有值的情況下返回nil:
if let removedValue = airports.removeValueForKey("DUB") {print("The removed airport's name is \(removedValue).") } else {print("The airports dictionary does not contain a value for DUB.") } // prints "The removed airport's name is Dublin Airport."字典遍歷
我們可以使用for-in循環(huán)來(lái)遍歷某個(gè)字典中的鍵值對(duì)。每一個(gè)字典中的數(shù)據(jù)項(xiàng)都以(key, value)元組形式返回,并且我們可以使用臨時(shí)常量或者變量來(lái)分解這些元組:
for (airportCode, airportName) in airports {print("\(airportCode): \(airportName)") } // YYZ: Toronto Pearson // LHR: London Heathrow通過(guò)訪問(wèn)keys或者values屬性,我們也可以遍歷字典的鍵或者值:
for airportCode in airports.keys {print("Airport code: \(airportCode)") } // Airport code: YYZ // Airport code: LHRfor airportName in airports.values {print("Airport name: \(airportName)") } // Airport name: Toronto Pearson // Airport name: London Heathrow如果我們只是需要使用某個(gè)字典的鍵集合或者值集合來(lái)作為某個(gè)接受Array實(shí)例的 API 的參數(shù),可以直接使用keys或者values屬性構(gòu)造一個(gè)新數(shù)組:
let airportCodes = [String](airports.keys) // airportCodes 是 ["YYZ", "LHR"]let airportNames = [String](airports.values) // airportNames 是 ["Toronto Pearson", "London Heathrow"]Swift 的字典類(lèi)型是無(wú)序集合類(lèi)型。為了以特定的順序遍歷字典的鍵或值,可以對(duì)字典的keys或values屬性使用sort()方法。
總結(jié)
以上是生活随笔為你收集整理的Swift傻傻分不清楚系列(六)集合类型的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Swift傻傻分不清楚系列(五) 字符串
- 下一篇: Swift傻傻分不清楚系列(七)控制流