Swift 程式語言 — 集合類型(2) — Set & Dictionary
讓我們來看看剩下的兩種無序的集合類型,Set 以及 Dictionary 吧!
前言:
前面我們介紹到了集合類型中的 Array
,這次教學我們要來繼續講解剩下的兩種無序的集合類型 Set
和 Dictionary
。
⎮ Set
Set
在集合中儲存相同類型的不同值,並且沒有定義其順序。當你需要確認項目只出現一次或是項目中的順序是不重要時,你可以使用 Set
而不是 Array
。
● Set 類型中的 HashValue
類型必須是能 hashable
才能儲存在 Set
中,也就是類型必須提供計算自身的 hashValue
的方法。HashValue
是一個 Int
類型的值,對於同等比較所有物件都是相同的。也就是下列這段程式碼的意思:
如果 a == b , 那麼 a.hashValue == b.hashValue
默認情況下,所有 Swift 的基礎類型都是能 hashable
的(String
、Int
、Double
和 Bool
),並且可以用於設置 Set 的值類型或是 Dictionary
的 key
類型。Enumeration
中沒有關聯值的 case
也是能 hashable
的。
你可以使用你自定義的類型透過遵循 Swift 基本庫的 Hashable 協議來作為 Set 值類型或是 Dictionary 的 key 類型。遵循 Hashable 的類型必須提供名為 hashValue 的 Int 屬性。類型中的
hashValue
返回值不需要在同一個程序中的不同執行階段或不同程序中相同。因為 Hashable 協議遵循符合 Equatable,所以遵循的類型還必須提供等於運算符(==)的實現,Equatable 協議要求任何遵循的實現都必須是等價的關係。也就是說,對於所有值 a, b, c 想等的實現必須滿足下列三個條件。
a == a 自反性 (Reflexivity)
a == b implies b == a 對稱性 (Symmetry)
a == b && b == c implies a == c 傳遞性 (Transitivity)
● Set 類型語法:
你可以透過下列方式宣告一個空的 Set
,它不像 Array
有簡寫的方式。
let someInts = Set<Int>()
let someDoubles: Set<Double> = []
● 創建一個空的 Set:
Array 和 Set 在很多地方其實很雷同,包含其方法、操作等等。但是還是要了解其差別,並不是所有地方都相同。
我們一樣可以透過上列方式來創建一個空的 Set
,我們一樣可以透過 isEmpty
來確認是否為空的 Set
,以及使用 count
來查看集合數量:
我們一樣可以直接賦予他一個 []
使它變成一個空的 Set
:
● 創建一個有預設值的 Set:
我們也可以透過給他預設值的情況,設置一個 Set,我們可以使用以下方式來創建他:
let someInts: Set = [1, 2, 3]
let someStrings: Set<String> = ["Hello", "World"]
● Set 的插入、刪除以及是否包含 :
這邊我們跟 Array
一樣能夠執行與插入,差異點是在於因為 Set
是無序排列,所以我們不需要也無法告訴它插入和刪除位置(index
):
刪除也是如此,我們只需要告訴該元素名稱即可:
接著我們可能需要知道 Set 中是否包含著某個元素,我們可以使用 contains(member:)
來檢查:
● 遍歷 Set:
這邊我們一樣可以使用 for-in
迴圈來遍歷 Set
:
當你想要 Set
裡面能夠有序排列時,我們可以透過 sorted()
方法,讓 Set
跟據預設或是自定義的規則來執行排序,讓該 Set
變成有序排列。也因為它變成有序排列,所以 Set.sorted()
也就等同於 Array
:
● 執行 Set 操作:
你可以有效地去執行基本的 Set
操作,例如:結合兩個 Set
,判斷兩個 Set
有哪些值,或者確定兩個 Set
是否包含全部、一些或是不包含的值。
- 使用
intersection(_:)
方法來創建一個只包含兩個Set
共有值的Set
- 使用
symmetricDifference(_:)
方法來創建一個只包含兩個Set
各自有的且沒有共有值的Set
- 使用
union(_:)
方法來創建一個包含兩個Set
所有值的Set
- 使用
subtracting(_:)
方法來創建一個兩個Set
當中不包含某個Set
的Set
這邊借官方的範例來使用,這裡我們有三種不同的 Set:
// 基數 Set
let oddDigits: Set = [1, 3, 5, 7, 9]
// 偶數 Set
let evenDigits: Set = [0, 2, 4, 6, 8]
// 隨機數 Set
let singleDigitPrimeNumbers: Set = [2, 3, 5, 7]
然後配合上面四種方法操作後的結果如下,最後使用 sorted()
排列:
oddDigits.union(evenDigits).sorted()
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] oddDigits & evenDigits 所有值。oddDigits.intersection(evenDigits).sorted()
// [] oddDigits & evenDigits 求共同值oddDigits.subtracting(singleDigitPrimeNumbers).sorted()
// [1, 9] oddDigits 去除 singleDigitPrimeNumbers 共同值oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted()
// [1, 2, 9] // oddDigits & singleDigitPrimeNumbers 所有值去除兩者共同值
● Set 成員關係以及相等性:
下圖描繪了三個 Set
(a, b, c),其中重疊區域表示之間共享的元素。Set a
是 Set b
的 superset
,因為 a 包含 b 中的所有元素。相反的 Set b
是 Set a
的子集,因為 b 中所有的元素也包含在 a 中。Set b
和 Set c
彼此不相交,因為他們沒有共同的元素。
- 使用 “相等” 運算符 (==)來判斷兩個
Set
是否包含相同的值 - 使用
isSubset (of:)
方法來確定一個Set
的所有值是被某Set
包含,是否為其subset
- 使用
isSuperset(of:)
方法來確定一個Set
是否包含某個Set
的所有值,是否為其superset
- 使用
isStrictSubset(of:)
或者isStrictSuperset(of:)
方法來確定是個Set
是否為某一個Set
的subset
或者superset
,但並不相等; - 使用
isDisjoint(with:)
方法來判斷兩個Set
是否擁有完全不同的值。
let houseAnimals: Set = ["🐶", "🐱"]
let farmAnimals: Set = ["🐮", "🐔", "🐑", "🐶", "🐱"]
let cityAnimals: Set = ["🐦", "🐭"]
這邊我們有三種不同動物的 Set
,讓我們測試一下上述這幾種方法吧,可以使用 Playground
或是 Command Line
來練習:
⎮ Dictionary
Dictionary
儲存類型相同的值與類型相同的 key
之間的關聯,沒有定義的排序。每個值都與唯一的 key
關聯,這個 key
在 Dictionary
中做為這個值的識別符(identifier)。不同於 Array
中的項目,Dictionary
的項目沒有特定的順序,為無序集合類型。當你需要識別符查找某個項目時,可以使用 Dictionary
。
● 創建空的 Dictionary:
let dictionaryOne = [Int: String]()
let dictionaryTwo: [Int: String] = [:]
這邊可以看到我們兩個 Dictionary
的 key
的類型為 Int
,其值類型為 String
。你可以透過一個 [:]
賦予 Dictionary
一個空的 Dictionary
值。
● 用字典字面量 (Dictionary Literal)創建 Dictionary
我們可以提供 Dictionary Literal
作為 Dictionary
的預設值,來創建我們的 Dictionary
,每個項目的組合為 key:value
,每個項目須以 ,
來作為間隔:
[key 1: value 1, key 2: value 2, key 3: value 3]
我們在提供預設值後,也能夠省略常數或變數的後的類型標註,該 Dictionary
類型會根據其 key:value
個別的類型推斷出來:
● 訪問以及修改 Dictionary
我們一樣可以使用 count
屬性來查看 Dictionary
中的項目數:
還有使用 isEmpty
屬性來查看是否為空的 Dictionary
:
你可以透過其該項目的 key 值來訪問項目值,但是你會發現它會是個 Optional
類型的值:
這是因為 Dictionary
無法確定你所給它的這個 key
值是否能夠找到對應的值,若是它無法找到對應的值,就會返回一個 nil
。
所以當我今天想要修改某個項目的值我可以透過其 key
來找出對應的值,如果找不到這個 key
的話,則會在 Dictionary
中加入這組 key-value
的項目:
我們也能使用 Dictionary
中的 updateValue (value:,key:)
的方式來更新 key
所對應的值。如同上面的操作相同,如果找到對應 key 的值,就進行值的更新,反之則會新增:
我們可以使用 myDictionary.removeValue(forKey:)
來刪除 Dictionary
中 key
所對應的值,也能夠過賦予 key
的對應值一個 nil
來清除項目:
我們也能透過 Optional Binding
來判斷是否 Dictionary
中有沒有 key
所對應的值:
在我們透過 updateValue(value:forKey:)
或是 removeValue(forKey:)
修改 Dictionary
時,同時也會返回給我們修改的項目,若沒有被修改則會返回給我們 nil
,所以這邊也能使用 Optional Binding
來判斷是否有被修改:
● 遍歷 Dictionary
當然我們也能使用 for-in
迴圈來查看 Dictionary
中的所有項目,並且查看其項目的 key
和 value
:
我們也可以透過一個 Tuple
作為複合值來個別印出:
當然我們也能夠選擇只去 for-in
循環 Dictionary
中的 key
或是 value
:
如果你需要使用帶有 Array
實例的 API 的 Dictionary
的 keys
或 values
,那麼我們可以使用 Dictionary
的 keys
或是 values
來初始化一個新的 Array
:
後記:
那麼我們集合類型的教學就到這邊告一段落了,希望大家在之後使用這三種集合類型的時候,能夠根據你的需求來選擇適合的集合類型,然後也能夠活用這三種集合類型的方法,能夠新增、刪除修改類型內的資料。如果你能夠講出三種集合類型的相似點以及不同點,那麼你就對於這三者的特性有一定的理解了。