Collection Types Of Swift
前言
Swift語言提供Array、Set和Dictionary三種基本的集合類型用來存儲集合數據。數組是有序的數據集;集合是無序無重復的數據集;而字典是無序的鍵值對數組集。
Swift的Array、Set和Dictionary類型被實現為泛型集合。因此,它所存儲的元素的類型必須是一致的,同樣,我們取出來的數據類型也是明確的。
集合的可變性(Mutability Of Collections)
如果創建一個Arrays、Sets或Dictionaries并且把它分配成一個變量,這個集合將會是可變的。這意味著我們可以在創建之后添加更多或移除已存在的數據項,或者改變集合中的數據項。
如果我們把Arrays、Sets或Dictionaries分配成常量,那么它就是不可變的,它的大小和內容都不能被改變。
若我們確定集合不需要改變,那么我們應該使用不可變集合,也就是使用 let 聲明,那樣編譯器會為我們優化。
// 在swift中要讓Array、Set、Dictionary類型的變量是可變的,用var聲明就可以了。 // 如果不希望可變,用let聲明即可。 letimmutableArrray = [Int]() // 提示:error: immutable value of type '[Int]' only has mutating // members named 'append' //immutableArrray.append(12) // OK varmutableArray = [Int]() mutableArray.append(12)
數組(Arrays)
數組使用有序列表存儲同一類型的多個值,相同的值可以多次出現在一個數組的不同位置中。我們需要明確,數組是有序的,元素類型是相同的。
Swift中的Array與Foundation中的NSArray是橋接的,可以相互轉換。
創建一個空數組(Create An Empty Array)
由于Swift中的數組是泛型,因此必須指定元素的數據類型:
// 使用構造語法來創建一個由特定數據類型構成的空數組: // 方括號中必須指定類型,因為Array是一種泛型 varemptyArray = [Int]() // 也可以使用結構體Array的構造寫法 varanotherEmptyArray = Array<Int>() // 如果之前類型明確了,直接再用[]即可.一樣是Int類型的元素,是可推斷出來的 emptyArray = []
創建帶默認值的數組(Creating An Array With A Default Value)
Swift提供了如下方法,可以創建帶有默認值的數組,這個構造方法是使用count個指定元素來初始化:
/// Construct a Array of `count` elements, each initialized to /// `repeatedValue`. publicinit(count: Int, repeatedValue: Element)
例如:
varthreeDoubles = [Double](count: 3, repeatedValue:0.0) // threeDoubles 是一種 [Double] 數組,等價于 [0.0, 0.0, 0.0]
數組相加來創建數組(Creating An Array By Adding Two Array Togeter)
兩個數組能夠通過加號 + 來創建一個合并的數組,是因為蘋果為我們提供了這樣的方法:
publicfunc +<RRC1 : RangeReplaceableCollectionType, RRC2 : RangeReplaceableCollectionType whereRRC1.Generator.Element == RRC2.Generator.Element>(lhs: RRC1, rhs: RRC2) -> RRC1
這是個泛型函數,要求類型RRC1和RRC2是遵守 RangeReplaceableCollectionType 協議的,并且這兩個集合的元素類型要求相同。而數組是遵守 RangeReplaceableCollectionType 協議的,因此可以通過此方法將兩個數組相加。
例如:
// anotherThreeDoubles 被推斷為 [Double],等價于 [2.5, 2.5, 2.5] varanotherThreeDoubles = Array(count: 3, repeatedValue: 2.5) // sixDoubles 被推斷為 [Double],等價于 [0.0, 0.0, 0.0, 2.5, 2.5, 2.5] varsixDoubles = threeDoubles + anotherThreeDoubles
用數組字面量構造數組(Creating An Array With An Array Literal)
我們可以使用字面量數組來進行數組構造,這是一種用一個或者多個數值構造數組的簡單方法。字面量是一系列由逗號分割并由方括號包含的數值:
// shoppingList 已經被構造并且擁有兩個初始項。 var shoppingList: [String] = ["Eggs", "Milk"]
由于類型是明確的,因此我們可以交由編譯器來推斷類型數組的類型:
varshoppingList = ["Eggs", "Milk"]
訪問和修改數組(Accessing And Modifying An Array)
常見的數組訪問和修改操作有:
- 獲取數組元素個數
- 判斷數組是否為空
- 數組追加元素
- 數組在某個位置插入元素
- 數組在某個范圍插入元素
數組元素個數
獲取數組元素的個數,可通過 count 屬性獲取:
// 打印結果:"The shopping list contains 2 items." print("The shopping list contains \(shoppingList.count) items.")
數組判斷是否為空
判斷數組是否為空,可以通過 isEmpty 屬性判斷,也可以通過 count 判斷:
if shoppingList.isEmpty { print("The shopping list is empty.") } else { print("The shopping list is not empty.") } // 或者通過count屬性 if shoppingList.count == 0 { print("The shopping list is empty.") } else { print("The shopping list is not empty.") }
數組末尾追加元素
常用的有以下幾種方法:
- 通過 append() 追加
- 通過 appendContentsOf() 追加
- 通過 += 追加
我們先來分析一下數組結構體所提供的方法:
/// Append the elements of `newElements` to `self`. /// /// - Complexity: O(*length of result*). publicmutatingfuncappendContentsOf<S : SequenceType where S.Generator.Element == Element>(newElements: S) /// Append the elements of `newElements` to `self`. /// /// - Complexity: O(*length of result*). publicmutatingfuncappendContentsOf<C : CollectionType where C.Generator.Element == Element>(newElements: C)
我們可以看到對于第一個 appendContentsOf 方法要求參數 S 是遵守了 SequenceType :
vararray = [1, 2] // 追加單個元素 array.append(3) // 追加一個數組 array += [4, 5] // 追加一個集合 array.appendContentsOf([6, 7]) // 追加一個序列:1,2,3,4,5,6 array.appendContentsOf(1...6)
數組插入元素
數組提供了 insert(_:at:) 方法插入數據:
/// Insert `newElement` at index `i`. /// /// - Requires: `i <= count`. /// /// - Complexity: O(`count`). publicmutatingfuncinsert(newElement: Element, atIndex i: Int)
那么可以通過調用來插入數據:
vararray = [1, 2] array.insert(55, atIndex: 2)
數組修改值
我們也可以通過下標獲取或者修改數據,我們先看看聲明:
// 這個方法是在index位置插入數據或者獲取index位置的數據 publicsubscript (index: Int) -> Element // 這個方法是在指定的range插入多個數據或者獲取指定range的數據 publicsubscript (subRange: Range<Int>) -> ArraySlice<Element>
通過下標獲取或者修改值:
// 修改元素值,可以通過索引 array[0] = 100 // 獲取 print(array[0]) // 可以修改指定范圍的值 array[1..<4] = [77, 88, 99] // 獲取 print(array[1..<4])
也可以調用 replaceRange(_:with:) 來替換值:
array.replaceRange(1...2, with: [10, 12])
數組移除元素
先看看蘋果所提供的方法:
// 移除最后一個元素 publicmutatingfuncremoveLast() -> Element // 某個指定位置的元素 publicmutatingfuncremoveAtIndex(index: Int) -> Element // 移除某個范圍 publicmutatingfuncremoveRange(subRange: Range<Self.Index>) // 移除前n個 publicmutatingfuncremoveFirst(n: Int) // 移除第一個 publicmutatingfuncremoveFirst() -> Self.Generator.Element // 清空 publicmutatingfuncremoveAll(keepCapacity keepCapacity: Bool = default)
看看如何調用:
// 刪除第一個 letelement = array.removeAtIndex(0) // 刪除最后一個 letlastElement = array.removeLast() // 刪除所有元素,但是內存不釋放,空間容量保留 array.removeAll(keepCapacity: true) // 刪除所有元素,且不保留原來的空間 array.removeAll(keepCapacity: false) // 移除位置1、2、3的元素 array.removeRange(1...3) // 移除第一個元素 letfirst = array.removeFirst() // 移除前三個元素 array.removeFirst(3)
數組遍歷
常用的數組遍歷有幾種。
第一種,只獲取元素值的for-in遍歷:
foritemin array { print(item) }
第二種,獲取元素及索引的for-in遍歷:
for (index, value) in array.enumerate() { print("index: \(index), value = \(value)") }
集合(Sets)
集合(Set)用來存儲相同類型并且沒有確定順序的值。當集合元素順序不重要時或者希望確保每個元素只出現一次時可以使用集合而不是數組。
為了存儲在集合中,該類型必須是可哈希化的。也就是說,該類型必須提供一個方法來計算它的哈希值。一個哈希值是Int類型的,它和其他的對象相同,其被用來比較相等與否,比如a==b,它遵循的是a.hashValue == b.hashValue。Swift 的所有基本類型(比如String,Int,Double和Bool)默認都是可哈希化的,它可以作為集合的值或者字典的鍵值類型。沒有關聯值的枚舉成員值(在枚舉有講述)默認也是可哈希化的。
使用自定義的類型作為集合的值或者是字典的鍵值類型時,需要使自定義類型服從Swift標準庫中的Hashable協議。服從Hashable協議的類型需要提供一個類型為Int的取值訪問器屬性hashValue。這個由類型的hashValue返回的值不需要在同一程序的不同執行周期或者不同程序之間保持相同。 因為hashable協議服從于Equatable協議,所以遵循該協議的類型也必須提供一個 是否等
運算符(==)的實現。這個Equatable協議需要任何遵循的==的實現都是一種相等的關系。也就是說,對于a,b,c三個值來說,==的實現必須滿足下面三種情況:
- a==a(自反性)
- a==b 可推出 b==a(對稱性)
- a==b&&b==c 可推出 a==c(傳遞性)
空集合(Empty Set)
Swift 中的 Set 類型被寫為 Set<Element> ,這里的 Element 表示 Set 中允許存儲的類型,和數組不同的是,集合沒有等價的簡化形式。
varemptySet = Set<Int>() print(emptySet.count) // 0 // 當變量已經有明確的類型時,再用空數組來初始化[],其類型也是可推斷的 emptySet = []// 類型為Set<Int>()
初始化集合(Initializing An Empty Set)
可以直接使用數組字面量來初始化集合:
// 用數組構造集合 var favSet: Set<String> = ["milk", "egg"] // 如果元素都是相同類型,帶有初始化,可以省略類型 var favSet1: Set = ["good milk", "bad egg"]
使用集合可以非常好地解決重復問題:
// 重復的數據會被自動去重 var favSet: Set<String> = ["milk", "egg", "egg", "milk"] print(favSet.count)// 2
訪問集合(Accessing A Set)
獲取集合元素的個數,可以通過 count 只讀屬性獲取,其時間復雜度為O(1),也就是常量,因此效率是極高的。想想時間復雜度能達到O(1),說明內部并不是列表,而是類似字典那樣的哈希表,不過具體內部是如何,這里不細說,還得去查看源代碼才能知曉。
/// The number of members in the set. /// /// - Complexity: O(1). publicvarcount: Int { get }
調用就很方便了,效率又非常地高:
letcount = favSet.count
我們還可以通過 isEmpty 屬性判斷是否為空:
// 官方聲明如下: /// `true` if the set is empty. publicvarisEmpty: Bool { get } // 判斷是否為空集 if favSet.isEmpty { print("empty set") } else { print("non empty set") }
判斷元素是否在集合中:
// 判斷是否包含元素 if favSet.contains("wahaha") { print("fav set contains wahaha") } else { print("fav set doesn\'t not contain wahaha") }
遍歷集合(Iterating Over A Set)
遍歷很少能離開得了 for-in 語法:
// 遍歷一個set。使用for-in forvaluein favSet { print(value)// 這是無序的 }
遍歷集合,同時使集合是有序的:
// 要使之有序 // sort方法 forvaluein favSet.sort({ (e, ee) -> Bool in return e > ee // 由我們指定排序類型desc(降序) }) { // milk // egg print(value) } // 或者: // 或者直接使用sort無參數(默認為asc升序) forvaluein favSet.sort() { // egg // milk print(value) }
當然還可以使用最原始的方法來遍歷:
forvar i = favSet.startIndex; i != favSet.endIndex; i = i.successor() { print("for-i-in" + favSet[i]) }
集合基本操作(Fundamental Set Operations)
集合的基本操作有:
- 求交集:使用intersect(_:)方法根據兩個集合中都包含的值創建的一個新的集合。
- 求非交集:使用exclusiveOr(_:)方法根據在一個集合中但不在兩個集合中的值創建一個新的集合。
- 求并集:使用union(_:)方法根據兩個集合的值創建一個新的集合。
- 求差集:使用subtract(_:)方法根據不在該集合中的值創建一個新的集合。
let set1: Set = [1, 2, 3, 4, 5] let set2: Set = [1, 4, 3, 8] // 求兩個集合的交集 // 3, 1, 4 print(set1.intersect(set2)) // 求兩個集合的并集 // 1, 2, 3, 4, 5 print(set1.union(set2).sort()) // 求集合set1去掉set1與set2的交集部分 // 2, 5 print(set1.subtract(set2).sort()) // 求集合set1與set2的并集 去掉 集合set1與set2的交集 部分 // 2, 5, 8 print(set1.exclusiveOr(set2).sort())
集合成員關系(Relationships Of Set members)
- 使用==來判斷兩個集合是否包含全部相同的值。
- 使用isSubsetOf(_:)方法來判斷是否為子集
- 使用isSupersetOf(_:)方法來判斷是否為超集
- 使用isStrictSubsetOf(_:)或者isStrictSupersetOf(_:)方法來判斷一個集合是否是另外一個集合的子集合或者父集合并且兩個集合并不相等。
- 使用isDisjointWith(_:)方法來判斷兩個集合是否不含有相同的值。
從圖中可以看出:
- 集合a全部包含b,也就是說b是a的子集
- 集合a與集合b相交,但是a與b不完全包含
- 集合c與集合b完全沒有交集
let set1: Set = [1, 2, 3, 4, 5] let set2: Set = [1, 4, 3, 8] // false // set1并沒有全部被set2包含 print(set1.isSubsetOf(set2)) let set3: Set = [1, 2, 5] // true // set3全部在set1中 print(set3.isSubsetOf(set1)) // true // set1真包含set3,因此set1是set3的超集 print(set1.isSupersetOf(set3)) // true // set1是set3的嚴格超集,因為Set1真包含set3且set1 != set3 print(set1.isStrictSupersetOf(set3)) // true // set1真包含set3,且set1 != set3,因此set3是set1嚴格的真子集 print(set3.isStrictSubsetOf(set1)) let set4: Set = [1, 5, 2] // false // 因為set3和set4的元素是一樣的,即相等 print(set4.isStrictSubsetOf(set3)) // false // 這兩個集合中有相同的元素 print(set3.isDisjointWith(set4)) let set5:Set = [9, 6] // true // set5和set4沒有相同的元素 print(set5.isDisjointWith(set4))
字典(Dictonaries)
字典是一種存儲多個相同類型的值的容器。每個值都關聯唯一的鍵,鍵作為字典中的這個值數據的標識符。和數組中的數據項不同,字典中的數據項并沒有順序的。我們在需要通過key訪問數據的時候使用字典,這種方法很大程度上和我們在現實世界中使用字典查字義的方法一樣。
Swift中的Dictionary與Foundation中的NSDictionary是橋接的,可以互相轉換使用。
我們先來看看字典的結構定義:
/// A hash-based mapping from `Key` to `Value` instances. Also a /// collection of key-value pairs with no defined ordering. publicstruct Dictionary<Key : Hashable, Value> : CollectionType, DictionaryLiteralConvertible
它是基于哈希表從key到value實例的映射,它的鍵值是無序的。要求key是遵守Hashable的。 其正規語法應該是: Dictonary<key, value> 。
創建空字典(Creating An Empty Dictionary)
// 創建空字典 varemptyDict = [Int: String]() // 或者 emptyDict = Dictionary<Int, String>() // 當創建過emptyDict后,再用[:]又成為一個空字典 emptyDict = [:]
初始化字典(Initializing A Dictionary)
// 多個鍵值對用逗號分割 // 通過類型推斷是可以推斷出來類型為[String: String] vardict = ["A": "Apple", "B": "Bubble"] // 手動指明數據類型 var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
聲明不可變字典:
// 如果不希望字典可變,用let聲明即可 letimmutableDict = ["A": "Apple", "B": "Bubble"] // 無法調用,因為是不可變的 //immutableDict.updateValue("App", forKey: "A") //immutableDict += ["AB:", "Apple Bubble"]
訪問和修改字典(Accessing And Modifying A Dictionary)
我們可以通過字典的方法和屬性來訪問和修改字典,或者通過使用下標語法。
判斷是否為空字典:
if dict.isEmpty { print("dict is an empty dictionary") } else { print("dict is not an empty dictionary") } // 或者通過獲取字典元素的個數是否為0 if dict.count == 0 { print("dict is an empty dictionary") } else { print("dict is not an empty dictionary") }
可以直接通過下標獲取元素:
// 通過key直接獲取 varapple = dict["A"] // 獲取索引獲取(key, value),再獲取值 // 由于字典中的(key, value)是元組,因此可以直接通過點語法獲取 letstartKeyValue = dict[dict.startIndex] let value = startKeyValue.1 // 讀取時,判斷是否有值 if letvalue = dict["BB"] { print("value is \(value)") } else { // 輸入這行 print("The key BB doesn\'t not exist in dict") }
可以直接通過下標來修改或者增加值:
// 修改,如果不存在,則會增加,否則更新值 dict["A"] = "Good Apple" // 或者用API,如果不存在,則會增加,否則只是更新值 if letoldValue = dict.updateValue("Dog",forKey: "D") { print("The old value is \(oldValue)") } else { // 輸出這行 print("The key D does\'t not exist before update") }
移除元素:
// 通過下標語法移除元素 dict["D"] = nil // 或者通過調用removeValueForKey方法來移除 if letremovedValue = dict.removeValueForKey("D") { print("The removed value is \(removedValue)") } else { print("The key D doesn'\t exist in dict, can\' be removed.") } // 或者清空字典: dict.removeAll()
字典遍歷(Iterate Over A Dictionary)
字典中每個元素是一個鍵值對,而這個鍵值對其實就是一個元組:
// 用for-in遍歷,形式為(key, value) for (key, value) in dict { print("\(key): \(value)") }
可以通過遍歷所有的key:
// 可以遍歷key forkeyin dict.keys { print("\(key): \(dict[key])") }
也可以遍歷所有的值:
forvaluein dict.values { print("\(value)") }
字典值或者鍵轉換成數組
如果不清楚Array有這么一個構造函數,那么您可能會手動寫代碼去遍歷,但是事實上我們可以直接使用構造函數來實現的。我們知道字典的keys和values屬性返回來的是 LazyMapCollection<key, value> 類型,并不是我們直接需要到的數據,因此我們有的時候需要轉換成我們直接能使用的數據,這時候就需要這么做的:
// 獲取所有的值 letvalues = [String](dict.values) // 獲取所有的鍵 letkeys = [String](dict.keys)
寫在最后
本篇博文是筆者在學習Swift 2.1的過程中記錄下來的,可能有些翻譯不到位,還請指出。另外,所有例子都是筆者練習寫的,若有不合理之處,還望指出。
學習一門語言最好的方法不是看萬遍書,而是動手操作、動手練習。如果大家喜歡,可以關注哦,盡量2-3天整理一篇Swift 2.1的文章。這里所寫的是基礎知識,如果您已經是大神,還請繞路!
文章并非純翻譯,而是練習語法而記錄下來的內容
關注我
如果在使用過程中遇到問題,或者想要與我交流,可加入有問必答 QQ群: 324400294
關注微信公眾號: iOSDevShares
關注新浪微博賬號:標哥Jacky
支持并捐助
如果您覺得文章對您很有幫助,希望得到您的支持。您的捐肋將會給予我最大的鼓勵,感謝您的支持!
支付寶捐助 | 微信捐助 |
---|---|
![]() |
![]() |