【Swift 3.1】04 - 集合類型 (Collection Types)

集合類型 (Collection Types)

自從蘋果2014年發布Swift,到現在已經兩年多了,而Swift也來到了3.1版本。去年利用工作之余,共花了兩個多月的時間把官方的Swift編程指南看完。現在整理一下筆記,回顧一下以前的知識。有需要的同學可以去看官方文檔>>


Swift提供了三種集合類型:數組(Array)、集合(Set)、字典(Dictionary)。Array是有順序的值的集合;Set是多個唯一的值的無序集合;Dictionary是無序的鍵值對集合。

注意:Swift的ArraySetDictionary都屬于泛型集合。

數組 (Array)

數組只能存儲相同類型的值。相同的值可以出現在數組的不同位置中。

數組類型的速記語法 (Array Type Shorthand Syntax)

一個數組的類型是這樣寫的:Array<Element>Element是數組元素值的類型,也可以簡寫成:[Element]

創建一個空數組 (Creating an Empty Array)
var someInt = [Int]()
print("someInts is of type [Int] with \(someInts.count) items.")
// Prints "someInts is of type [Int] with 0 items."

someInt被推斷為[Int]類型。

如果上下文已經提供了數組的類型,空素組還可以寫成[]

someInt.append(3)
// someInts now contains 1 value of type Int
someInt = []
// someInts is now an empty array, but is still of type [Int]
創建一個有默認值的數組 (Creating an Array with a Default Value)
var threeDoubles = Array(repeating: 0.0, count: 3)
// threeDoubles is of type [Double], and equals [0.0, 0.0, 0.0]
通過合并兩個數組來創建數組 (Creating an Array by Adding Two Arrays Together)
let anotherThreeDoubles = Array(repeating: 2.5, count: 3)
// anotherThreeDoubles is of type [Double], and equals [2.5, 2.5, 2.5]

var sixDoubles = threeDoubles + anotherThreeDoubles
// sixDoubles is inferred as [Double], and equals [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]
用字面值創建數組 (Creating an Array with an Array Literal)
var shoppingLis = ["Eggs", "Milk"]
// shoppingList has been initialized with two initial items
訪問和修改數組 (Accessing and Modifying an Array)

獲取數組的個數:

print("The shopping list contains \(shoppingList.count) items.")
// Prints "The shopping list contains 2 items."

判斷數組元素的個數是否為0:

if shoppingList.isEmpty {
    print("The shopping list is empty.")
} else {
    print("The shopping list is not empty.")
}

追加一個元素:

shoppingList.append("Flour")
// shoppingList now contains 3 items, and someone is making pancakes

使用加法賦值運算符添加更多元素:

shoppingList += ["Baking Powder"]
// shoppingList now contains 4 items
shoppingList += ["Chocolate Spread", "Cheese", "Butter"]
// shoppingList now contains 7 items

使用下標獲取元素:

var firstItem = shoppingList[0]
// firstItem is equal to "Eggs"

更改元素:

shoppingList[0] = "Six eggs"
// the first item in the list is now equal to "Six eggs" rather than "Eggs"

使用下標一次性更改多個元素,甚至要更改的元素個數可以不等于新數組的個數:

shoppintList[4...6] = ["Bananas", "Apples"]
// 用兩個替換三個

在特定的位置插入元素:

shoppingList.insert("Maple syrup", at: 0)
// shoppingList now contains 7 items
// "Maple Syrup" is now the first item in the list

刪除特定位置的元素,并且返回被刪除的元素:

let mapleSyrup = shoppingList.remove(at: 0)
// the item that was at index 0 has just been removed
// shoppingList now contains 6 items, and no Maple Syrup
// the mapleSyrup constant is now equal to the removed "Maple Syrup" string

刪除最后一個元素:

let apples = shoppingList.removeLast()
// the last item in the array has just been removed
// shoppingList now contains 5 items, and no apples
// the apples constant is now equal to the removed "Apples" string
遍歷整個數組 (Iterating Over an Array)

使用for-in遍歷:

for item in shoppingList {
    print(item)
}
// Six eggs
// Milk
// Flour
// Baking Powder
// Bananas

使用enumerated()方法遍歷,這個方法返回包含索引和索引對應的元素的多元組:

for (index, value) in shoppingList.enumerated() {
    print("Item \(index + 1): \(value)")
}
// Item 1: Six eggs
// Item 2: Milk
// Item 3: Flour
// Item 4: Baking Powder
// Item 5: Bananas

集合 (Sets)

集合中無順序地存儲了同一類型的值,并且里面的每一個值都是唯一的。在元素的順序不重要或者要求每一個元素都需要唯一的時候,可以使用集合,而不用數組。

集合類型的哈希值 (Hash Values for Set Types)

集合里面的元素類型必須hashable,也就是說,這個元素類型必須提供一個方法來計算他自己的哈希值。一個哈希值是一個用來判斷兩個對象是否相等的Int類型的整數。例如,如果a == b,那么a.hashValue == b.hashValue

所有Swift的基本類型(例如StringIntDoubleBool)默認都是hashable的,都可以作為集合的值類型或者字典的鍵類型。沒有關聯值的枚舉值默認也是hashable的。

我們可以自定義類型,并且遵循Hashable協議,作為集合或者字典鍵的值類型。自定義的類型必須提供一個能讀取的Int類型的屬性,并命名為hashValue。在不同的程序或者同一個程序運行多次中,不要求每次hashValue屬性返回的值都相等。

因為Hashable協議遵循Equatable協議,所以我們自定義的類型還需要提供一個相等運算符(==)的實現。Equatable協議要求每一個==的實現是一個等價關系。也就是說,==的實現必須滿足下面三個條件:

  • a == a (自反性)
  • a == b,說明b == a (對稱性)
  • a == b && b == c,說明 a == c (傳遞性)
集合類型語法 (Set Type Syntax)

使用Set<Element>來設置集合類型,Element是集合存儲的元素類型。

創建和初始化一個空集合 (Creating and Initializing an Empty Set)
var letters = Set<Character>()
print("letters is of type Set<Character> with \(letters.count) items.")
// Prints "letters is of type Set<Character> with 0 items."

letters被推斷為Set<Character>類型。

同樣地,如果上下文提供了集合的類型信息,可以使用[]來創建一個空的集合:

letters.insert("a")
// letters now contains 1 value of type Character
letters = []
// letters is now an empty set, but is still of type Set<Character>
使用數組字面值來創建一個集合 (Creating a Set with an Array Literal)
var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
// favoriteGenres has been initialized with three initial items

集合的類型不能通過數組的字面值來推斷,所以Set的類型必須明確聲明。但是因為Swift的類型推斷,如果用一個包含相同類型字面值的數組來初始化集合,我們可以不寫集合的類型。例如:

var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]

因為數組的全部字面值都是同一類型,所以Swfit能推斷出Set<String>favoriteGenres的正確類型。

訪問和修改集合 (Accessing and Modifying a Set)

使用count屬性獲取集合元素個數:

print("I have \(favoriteGenres.count) favorite music genres.")
// Prints "I have 3 favorite music genres."

使用isEmpty屬性判斷集合中元素的個數是否為0:

if favoriteGenres.isEmpty {
    print("As far as music goes, I'm not picky.")
} else {
    print("I have particular music preferences.")
}
// Prints "I have particular music preferences."

使用insert(_:)方法添加元素:

favoriteGenres.insert("Jazz")
// favoriteGenres now contains 4 items

使用remove(_:)刪除一個元素,并返回被刪除的元素,如果元素不存在,返回nil;使用removeAll()刪除全部元素:

if let removedGenre = favoriteGenres.remove("Rock") {
    print("\(removedGenre)? I'm over it.")
} else {
    print("I never much cared for that.")
}
// Prints "Rock? I'm over it."

判斷是否包含某個元素:

if favoriteGenres.contains("Funk") {
    print("I get up on the good foot.")
} else {
    print("It's too funky in here.")
}
// Prints "It's too funky in here."
遍歷整個集合 (Iterating Over a Set)
for genre in favoriteGenres {
    print("\(genre)")
}
// Jazz
// Hip hop
// Classical

Swift的集合類型沒有定義順序,我們可以使用sorted()方法來排序,這個方法使用<運算符將元素從小到大排列:

for genre in favoriteGenres.sorted() {
    print("\(genre)")
}
// Classical
// Hip hop
// Jazz

執行集合操作 (Performing Set Operations)

基本集合操作 (Fundamental Set Operations)

下圖是集合ab執行了不同的方法之后,得出的結果圖:

Fundamental Set Operations
  • 使用intersection(_:)方法得到兩個集合共有的元素,并用這些相同的元素創建一個新的集合
  • 使用symmetricDifference(_:)方法得到除了兩個集合共有的元素外的所有元素,并用這些相同的元素創建一個新的集合
  • 使用union(_:)方法得到兩個集合的所有元素,并用這些相同的元素創建一個新的集合
  • 使用subtracting(_:)方法減去與指定集合相同的元素后剩下的元素,并用剩下的元素創建一個新的集合
let oddDigits: Set = [1, 3, 5, 7, 9]
let evenDigits: Set = [0, 2, 4, 6, 8]
let singleDigitPrimeNumbers: Set = [2, 3, 5, 7]
 
oddDigits.union(evenDigits).sorted()
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
oddDigits.intersection(evenDigits).sorted()
// []
oddDigits.subtracting(singleDigitPrimeNumbers).sorted()
// [1, 9]
oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted()
// [1, 2, 9]
集合關系和相等性 (Set Membership and Equality)

下圖演示了三個集合:abc,重疊區域代表有相同的元素。集合a是集合b的父集合,因為a包含了b的所有元素;相反,ba的子集合。集合b和集合c互不相交,因為他們沒有相同的元素。

Set Membership and Equality
  • 使用“是否相等”運算符 (==)來判斷兩個集合的所有元素是否相等
  • 使用isSubset(of:)方法判斷集合的所有元素是否包含于指定集合
  • 使用isSuperset(of:)方法判斷集合是否包含指定集合的所有元素
  • 使用isStrictSubset(of:)或者isStrictSuperset(of:)方法判斷集合是否子集合或者父集合,但是不等于指定的集合
  • 使用isDisjoint(with:)方法判斷兩個集合是否有相同的元素
let houseAnimals: Set = ["??", "??"]
let farmAnimals: Set = ["??", "??", "??", "??", "??"]
let cityAnimals: Set = ["??", "??"]
 
houseAnimals.isSubset(of: farmAnimals)
// true
farmAnimals.isSuperset(of: houseAnimals)
// true
farmAnimals.isDisjoint(with: cityAnimals)
// true

Dictionaries (字典)

字典是一個無序集合中相同類型的鍵和相同類型的值的關聯。每一個值關聯著一個唯一的鍵。

字典類型速記語法 (Dictionary Type Shorthand Syntax)

使用Dictionary<Key, Value>來指定字典的類型。

注意:字典的Key類型必須遵循Hashable協議,就像集合的值一樣。

還可以是用簡短的形式[Key: Value]來指定字典的類型.

創建一個空字典 (Creating an Empty Dictionary)
var namesOfIntegers = [Int: String]()
// namesOfIntegers is an empty [Int: String] dictionary

如果上下文已經提供了類型信息,可以使用[:]來創建一個空字典:

namesOfIntegers[16] = "sixteen"
// namesOfIntegers now contains 1 key-value pair
namesOfIntegers = [:]
// namesOfIntegers is once again an empty dictionary of type [Int: String]
利用字典字面值來創建字典 (Creating a Dictionary with a Dictionary Literal)
var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
訪問和修改字典 (Accessing and Modifying Dictionary)

獲取字典鍵值對的個數:

print("The airports dictionary contains \(airports.count) items.")
// Prints "The airports dictionary contains 2 items."

判斷字典中鍵值對的個數是否為0:

if airports.isEmpty {
    print("The airports dictionary is empty.")
} else {
    print("The airports dictionary is not empty.")
}
// Prints "The airports dictionary is not empty."

使用下標語法添加新的鍵值對:

airports["LHR"] = "London Heathrow"
// the value for "LHR" has been changed to "London Heathrow"

還可以使用updateValue(_:forKey:)方法來設置或更新一個鍵對應的值,并返回一個可選類型的值。如果這個鍵不存在,那么就添加一個新的鍵值對,并返回nil;如果這個鍵存在,那么就更新這個鍵對應的值,并返回之前的舊值。這可以讓我們檢查鍵對應的值是否更新成功。

if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") {
    print("The old value for DUB was \(oldValue).")
}
// Prints "The old value for DUB was Dublin."

使用下標語法來獲取鍵對應的值:

if let airportName = airports["DUB"] {
    print("The name of the airport is \(airportName).")
} else {
    print("That airport is not in the airports dictionary.")
}
// Prints "The name of the airport is Dublin Airport."

使用下標語法并把鍵對應的值設置為nil來刪除一個鍵值對:

airports["APL"] = "Apple International"
// "Apple International" is not the real airport for APL, so delete it
airports["APL"] = nil
// APL has now been removed from the dictionary

另外,還可以使用removeValue(forKey:)方法來刪除一個鍵值對,如果存在,返回鍵對應的值;如果不存在,返回nil

if let removedValue = airports.removeValue(forKey: "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."
遍歷整個字典 (Iterating Over a Dictionary)
for (airportCode, airportName) in airports {
    print("\(airportCode): \(airportName)")
}
// YYZ: Toronto Pearson
// LHR: London Heathrow

使用keysvalues屬性來遍歷字典的所有鍵和所有值:

for airportCode in airports.keys {
    print("Airport code: \(airportCode)")
}
// Airport code: YYZ
// Airport code: LHR

for airportName in airports.values {
    print("Airport name: \(airportName)")
}
// Airport name: Toronto Pearson
// Airport name: London Heathrow

如果要使用字典的所有鍵和所有值,可以利用數組的API來創建:

let airportCodes = [String](airports.keys)
// airportCodes is ["YYZ", "LHR"]

let airportNames = [String](airports.values)
// airportNames is ["Toronto Pearson", "London Heathrow"]

Swift的字典類型沒有定義順序,為了遍歷經過排序的所有鍵和所有值,需要使用keysvalues屬性的sorted()方法。


第四部分完。下個部分:【Swift 3.1】05 - 控制流 (Control Flow)


如果有錯誤的地方,歡迎指正!謝謝!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容