級別: ★☆☆☆☆
標簽:「iOS」「Swift 5.1」「Set」「Array」「Dictionary」
作者: 沐靈洛
審校: QiShare團隊
集合類型
Swift提供三種主要的集合類型,稱為Array
,Set
和Dictionary
,用于存儲值的集合。Array
是有序的值的集合。Set
是唯一值的無序集合。Dictionary
是鍵值關聯的無序集合。
可變集合:
如果創建數組,集合或字典,并將其分配給變量,則創建的集合將是可變的。我們可以通過添加,刪除或更改集合中的項目來更改集合。如果將數組,集合或字典分配給常量,則該集合是不可變的,并且其大小和內容不能更改。
數組:
數組可以存儲相同類型的值,并且相同的值可以在數組不同位置多次出現。
- 數組類型的簡寫語法:a.
Array <Element>
b.[Element]
其中Element
是允許數組存儲的值的類型。其中[Element]
簡寫形式優選。 - 創建一個空的數組:
使用初始化語法創建某個類型的空數組:
//方式一
let intArray = Array<Int>()
print("intArray中有\(intArray.count)個元素")
//方式二
let intArray = [Int]()
print("intArray中有\(intArray.count)個元素")
如果上下文已經提供了數組中的類型信息,則數組存儲的值得類型就是確定的。
- 創建帶有初始值得數組:
Swift的Array
類型提供了一個初始化方法,用于創建一個特定大小的數組,并將其所有值設置為相同的默認值。
let defaultValueArray = Array.init(repeating: "你好", count: 3)
print(defaultValueArray)//!< ["你好", "你好", "你好"]
- 兩個數組相加:
使用加法運算符+
將兩個具有兼容類型的現有數組相加來創建新數組。新數組的類型是從我們添加的兩個數組的類型推斷出來的。這兩個相加的數組的類型必須要可以兼容,否則編譯器會報錯。
let defaultValueArray = Array.init(repeating: "你好", count: 3)
let secondValueArray = Array.init(repeating: "大佬", count: 2)
let thirdValueArray = defaultValueArray + secondValueArray
print(thirdValueArray)//!< ["你好", "你好", "你好", "大佬", "大佬"]
- 使用字面量元素初始化數組:
使用數組字面量元素初始化數組,語法:[value 1, value 2, value 3]
let literalArray = ["逗哥","刀哥"] //!< ["逗哥", "刀哥"] 有初值,可以類型推斷,不必聲明數組的類型
let literalArray1 : [Int] = [6,7]//!< [6, 7] 聲明了類型
- 數組的訪問和修改:
count
屬性:訪問數組元素的個數。isEmpty
屬性值Bool
類型數組判空,相較Array.count == 0
更高效。
if intArray.isEmpty {
print("intArray:\(intArray)中有\(intArray.count)個元素")//!< intArray:[]中有0個元素
}
使用append(_:)
方法拼接新項到數組的末尾
var appendArray = ["飛哥","強哥"]
appendArray.append("大哥")//!< ["飛哥", "強哥", "大哥"]
使用加法賦值運算符+=
追加一個或多個兼容項的數組:
var appendArray = ["飛哥","強哥"]
let array1 = ["finally","ok"]
let array2 = ["拯救","靜","\u{65}"]
appendArray += array1 //!< ["飛哥", "強哥", "finally", "ok"]
appendArray += array2 //!< ["飛哥", "強哥", "finally", "ok", "拯救", "靜", "e"]
使用下標從數組中取值:
let literalArray = ["逗哥","刀哥"] //!< ["逗哥", "刀哥"]
let firstStr = literalArray[0]
print(firstStr) //!< 逗哥
使用下標更改給定索引處的值:
var literalArray = ["逗哥","刀哥"] //!< ["逗哥", "刀哥"]
literalArray[0] = "加油"
print(literalArray) //!< ["加油", "刀哥"]
使用范圍下標一次性更改值,即使替換值的數組長度與要替換的范圍不同。但是必須不能越界:下標范圍必須在當前數組的有效范圍內。
var totalArray = ["飛哥", "強哥", "finally", "ok", "拯救", "靜", "e"]
totalArray[...3] = ["","","","?"]//!< ["", "", "", "?", "拯救", "靜", "e"]
totalArray[5..<7] = ["融合了"]//!< 5..<7有兩個元素 被替換成了一個 log:["", "", "", "?", "拯救", "融合了"]
print(totalArray)
使用insert(_:at :)
方法在指定索引處將指定項插入數組:
var literalArray = ["逗哥","刀哥"] //!< ["逗哥", "刀哥"]
literalArray.insert("妄言", at: 1)//!< ["逗哥", "妄言", "刀哥"]
print(literalArray)
使用remove(at :)
方法從數組中刪除指定項并返回刪除項
var literalArray = ["逗哥","刀哥"] //!< ["逗哥", "刀哥"]
literalArray.insert("妄言", at: 1)//!< ["逗哥", "妄言", "刀哥"]
let deleteStr = literalArray.remove(at: 0)//!< ["妄言", "刀哥"]
print(literalArray,"刪除了:\(deleteStr)")//!< ["妄言", "刀哥"] 刪除了:逗哥
使用removeLast()
從數組中刪除最終項并返回刪除項。以避免需要查詢數組的count
屬性.
var literalArray = ["逗哥","刀哥"] //!< ["逗哥", "刀哥"]
literalArray.removeLast()
//!< 也可以removeFirst()
literalArray.removeFirst()
- 數組的遍歷
使用for-in
循環遍歷
var literalArray = ["逗哥","刀哥"] //!< ["逗哥", "刀哥"]
for item in literalArray {
print(item,"", separator: " ", terminator: "")//!< 逗哥 刀哥
}
使用enumerated()方法迭代數組:同時遍歷各項的整數索引及其值。
返回由整數和各項的值組成的元組。
for item in literalArray.enumerated() {
print(item.1) //!< 逗哥 刀哥
print(item.0) //!< 0 1
print(item) //!< (offset: 0, element: "逗哥") (offset: 1, element: "刀哥")
}
集合:
Set
是唯一值的無序集合,這個特性可以讓我們在適當的場景下去使用。
- 集合類型的值(散列,哈希)
必須是可散列的類型才能存儲在集合中。 即:集合中存儲的類型必須提供能夠計算自身散列值的方法。哈希值是一個Int
值,對于所有相互比較相等的對象都是相同的,例如a == b
,則遵循a.hashValue == b.hashValue
。
默認情況下,Swift的所有基本類型,如String
,Int
,Double
和Bool
都是可哈希的,并且可以用作集合中的值類型或字典的key類型。默認情況下,沒有關聯值的枚舉類型也是可以哈希的。
注意點:我們可以使用自定義符合Hashable
協議的類型作為集合中的值類型或字典的key類型。符合Hashable
協議的類型必須提供名為hashValue
的Int
類型的可訪問的屬性。該自定義類型的hashValue
屬性在同一程序的不同執行或不同程序中執行時返回的值不需要相同。因為Hashable
協議繼承Equatable
協議,所以符合Hashable
協議的類型還必須提供運算符==
的實現。
Equatable
協議要求任何符合==
實現的,都是相等關系。==
的實現必須要滿足三個條件:反身性,對稱性,及物性。
比如實現Equatable
協議的類型的值a,b,c:
?反身性
表示:a == a
?對稱性
表示:a == b則b == a
?及物性
表示:a == b && b == c則 a == c - 集合類型語法:
Set <Element>
,其中Element是允許集合存儲的類型。與數組不同,集合沒有等效的簡寫形式。 - 創建空集合:
let emptySet = Set<Int>()
let emptyCharacterSet = Set<Character>()
如果上下文已經提供了集合中的類型信息,則集合中存儲的值得類型就是確定的。
- 使用數組創建集合
使用數組初始化集合,將一個或多個值寫入集合中。
var favoriteGenres :Set<String> = ["可是","怎么","能夠","如此"]
//! 由于Swift的類型推斷的存在,在上下文提供了類型信息后,可以簡寫為
var favoriteGenres :Set = ["可是","怎么","能夠","如此"]
- 集合的訪問和修改
count
屬性:訪問集合中元素的個數。isEmpty
Bool類型的屬性值用于集合判空。
if favoriteGenres.isEmpty {
print("favoriteGenres集合中沒有元素")
} else {
print("favoriteGenres集合有 \(favoriteGenres.count)個元素")
}
使用insert(_:)
方法添加一個新項到集合中:
favoriteGenres.insert("聰明")//!< ["聰明", "能夠", "可是", "怎么", "如此"]
使用remove(_ :)
方法從集合中刪除指定項并返回刪除項:如果該項目是該集合的成員,則刪除該項目,并返回已刪除的值,如果該集合不包含該項目,則返回nil
if let removeItem = favoriteGenres.remove("聰明") {
print("\(removeItem)此項已被移除")
} else {
print("favoriteGenres集合中沒有該項")
}
使用contains(_:)
方法,判斷集合中是否包含某一項。
if favoriteGenres.contains("好的") {
print("包含")
} else {
print("不包含")
}
- 集合的遍歷
使用for-in
循環遍歷
var favoriteGenres :Set = ["可是","怎么","能夠","如此"]
for item in favoriteGenres {
print(item,"",separator: " ", terminator: "")//!< 能夠 怎么 如此 可是
}
使用sorted()
方法,以特定順序遍歷集合的值,該方法將集合的元素使用<
運算符排序后的元素作為數組返回。
for item in favoriteGenres.sorted() {
print(item)
}
-
集合的操作
兩組集合a和b以陰影區域表示的各種集合操作的結果。
集合操作圖示
- 使用
intersection(_ :)
方法創建一個只包含兩個集合共有的值的新集合。 - 使用
symmetricDifference(_ :)
方法創建一個新集合,其中包含任一集合中的值,但不包含兩個集合共同的部分。 - 使用
union(_ :)
方法創建一個包含兩個集合中所有值的新集合。 - 使用
subtracting(_:)
方法創建一個值不在指定集中的新集。
let set1 : Set<Int> = Set.init(arrayLiteral: 1,5,7,2)
let set2 : Set<Int> = [2,4,6,8]
let set3 = set1.intersection(set2)//!< 預期:[2] 實際 [2]
//除了相同以外的其他元素
let set4 = set1.symmetricDifference(set2)//!< 預期:[1,5,7,4,6,8] 實際: [6, 4, 5, 7, 8, 1]
let set5 = set1.union(set2)//!< 預期[6, 4, 5, 7, 8, 1,2] 實際 [4, 5, 2, 8, 1, 7, 6]
//從set1中去除與set2有關的所有元素
let set6 = set1.subtracting(set2)//!< 預期[1,5,7] 實際[1, 7, 5]
- 集合之間的關系與相等判斷:三個集合a,b和c,集合a包含集合b中的所有元素,則稱集合a是集合b的超集,相反,集合b是集合a的子集。集合b和集合c沒有共同的元素。集合a和集合c有共同的元素,則集合a和集合c相交。
?使用運算符==
確定兩個集合是否包含所有相同的值。
?使用isSubset(of :)
方法確定集合的所有值是否包含在指定集合中。
?使用isSuperset(of :)
方法確定集合是否包含指定集合中的所有值。
?使用isStrictSubset(of :
)或isStrictSuperset(of :)
方法來確定集合是否是指定集合的??子集或超集,但不能判斷相等。
?使用isDisjoint(with :)
方法確定兩個集合是否沒有有共同的值。
let set : Set<Int> = [1,5,2,7]
let set1 : Set<Int> = Set.init(arrayLiteral: 1,5,7,2,8)
//! 判斷相等
if set == set1 {
print("相等")
} else {
print("不相等")
}
//!判斷子集與超集
if set.isStrictSubset(of: set1){//!< set是否是set1的子集
print("set是set1的子集")
}
if set1.isStrictSubset(of: set){//!< set1是否是set的超集
print("set1是set的超集")
}
//!< 判斷是否不相交
if set1.isDisjoint(with: set) {//!< `true`元素不相交 否則`false`
print("set1和set不相交")
} else {
print("set1和set相交")
}
字典:
- 字典類型的簡寫語法:
a.Dictionary <Key,Value>
。b.[Key:Value]
。其中Key
表示字典中鍵的值的類型,Value
表示存儲的值的類型。
字典Key
類型必須符合Hashable
協議,就像集合的值類型一樣。 - 創建一個空的字典:
//方式一
let namesOfIntegers :Dictionary<Int,String> = Dictionary<Int,String>.init()
let namesOfIntegers = Dictionary<Int,String>.init()
let namesOfIntegers = Dictionary<Int,String>()
//方式二
let namesOfIntegers = [Int:String].init() //!< [:]
let namesOfIntegers = [Int:String]()
//方式三:提供類型信息
let namesOfIntegers : [Int:String] = [:]
如果上下文已經提供了字典中的類型信息,則字典中的類型信息就是確定的。
- 創建帶初始值的字典:
let airports : [String:String] = [String:String].init(dictionaryLiteral: ("name","zhangfei"),("age","16"),("職業","將軍"))//!< ["職業": "將軍", "age": "16", "name": "zhangfei"]
簡寫語法:[key 1: value 1, key 2: value 2, key 3: value 3]
//鍵值類型都相同,swift樂行推斷,故省略字典類型
let airports = ["職業": "將軍", "age": "16", "name": "zhangfei"]
//以下類型的字典就不能類型推斷,必須聲明字典的鍵值類型
let airports : [String : Any] = ["職業": "將軍", "age": 16, "name": "zhangfei"]
- 字典的訪問和修改
count
屬性:訪問字典鍵值對的個數。isEmpty
屬性值Bool類型,字典判空
if airports.isEmpty {
print("airports是空的")
} else {
print("airports:\(airports)中有\(airports.count)個元素")//!< airports:["name": "zhangfei", "職業": "將軍", "age": "16"]中有3個元素
}
添加新項到字典中
//方式一.使用下標語法
var airports = ["job": "將軍", "age": "16", "name": "zhangfei"]
airports["sex"] = "男"
//方式二
var airports = ["job": "將軍", "age": "16", "name": "zhangfei"]
airports.updateValue("男", forKey: "sex")
print(airports)//!< ["sex": "男", "job": "將軍", "age": "16", "name": "zhangfei"]
更改與特定鍵關聯的值
//方式一.使用下標語法
var airports = ["job": "將軍", "age": "16", "name": "zhangfei"]
airports["job"] = "主公"
//方式二
var airports = ["job": "將軍", "age": "16", "name": "zhangfei"]
airports.updateValue("主公", forKey: "job")
print(airports)//!< ["age": "16", "job": "主公", "name": "zhangfei"]
上述示例中updateValue(_:forKey:)
方法,若是key
在字典中已存在,則更改其對應的值為新值,并且返回其對應的舊值。若key
在字典中不存在,則返回nil
,并且添加這個新的鍵值對到字典中,因為返回值可nil
故此方法的返回值是可選類型。
if let oldValue = airports.updateValue("男", forKey: "sex") {
print("key早就已經存在了,并且舊值為\(oldValue)")
} else {
print("key不存在,需添加到字典中")//!< log:key不存在,需添加到字典中
}
刪除字典中鍵值對:
//方式一.使用下標語法
var airports = ["job": "將軍", "age": "16", "name": "zhangfei"]
airports["job"] = nil
//方式二
if let value = airports.removeValue(forKey: "job") {
print("The value \(value) was removed.")//!< The value 將軍 was removed.
}
print(airports)//!< ["age": "16", "name": "zhangfei"]
上述示例中removeValue(_:forKey:)
方法,若是key在字典中已存在,則移除,并且返回該移除項的值。若key在字典中不存在,則返回nil,(不存在鍵值對,無法做移除)因為返回值可nil故此方法的返回值是可選類型。
- 字典的遍歷:
使用for-in
循環遍歷字典中的鍵值對。字典中的每項都會作為(key, value)
這樣的元組類型返回,并且我們可以再迭代的過程中將元組的成員分解為臨時常量或變量。
//! 方式一
for item in airports {
print("\(item.key)")
print("\(item.value)")
}
//! 方式二
for (key,value) in airports {
print("\(key)")
print("\(value)")
}
單獨遍歷其鍵或值
for key in airports.keys {
print(key)
}
for value in airports.values {
print(value)
}
keys
或values
的類型轉換為數組。不可強轉,類型不一致,需要重新初始化為Array
類型
let keysArray = [String](airports.keys)
let valuesArray = Array<String>.init(airports.values)
Swift的Dictionary
類型是無序的。要按特定順序遍歷字典的鍵或值,需要在字典的 keys
或values
屬性的基礎上使用sorted()
方法(升序),得到有序的keys或values。
參考資料:
swift 5.1官方編程指南
推薦文章:
iOS 解析一個自定義協議
iOS13 DarkMode適配(二)
iOS13 DarkMode適配(一)
2019蘋果秋季新品發布會速覽
申請蘋果開發者賬號的流程
Sign In With Apple(一)