集合類型 - Collection Types

2018-12-05 16:30 更新

集合類型 (Collection Types)

Swift 語言提供經(jīng)典的數(shù)組和字典兩種集合類型來存儲集合數(shù)據(jù)。數(shù)組用來按順序存儲相同類型的數(shù)據(jù)。字典雖然無序存儲相同類型數(shù)據(jù)值但是需要由獨有的標識符引用和尋址(就是鍵值對)。

Swift 語言里的數(shù)組和字典中存儲的數(shù)據(jù)值類型必須明確。 這意味著我們不能把不正確的數(shù)據(jù)類型插入其中。 同時這也說明我們完全可以對獲取出的值類型非常自信。 Swift 對顯式類型集合的使用確保了我們的代碼對工作所需要的類型非常清楚,也讓我們在開發(fā)中可以早早地找到任何的類型不匹配錯誤。

注意:
Swift 的數(shù)組結構在被聲明成常量和變量或者被傳入函數(shù)與方法中時會相對于其他類型展現(xiàn)出不同的特性。 獲取更多信息請參見泛型章節(jié)。

數(shù)組

數(shù)組使用有序列表存儲同一類型的多個值。相同的值可以多次出現(xiàn)在一個數(shù)組的不同位置中。

Swift 數(shù)組特定于它所存儲元素的類型。這與 Objective-C 的 NSArray 和 NSMutableArray 不同,這兩個類可以存儲任意類型的對象,并且不提供所返回對象的任何特別信息。在 Swift 中,數(shù)據(jù)值在被存儲進入某個數(shù)組之前類型必須明確,方法是通過顯式的類型標注或類型推斷,而且不是必須是class類型。例如: 如果我們創(chuàng)建了一個Int值類型的數(shù)組,我們不能往其中插入任何不是Int類型的數(shù)據(jù)。 Swift 中的數(shù)組是類型安全的,并且它們中包含的類型必須明確。

數(shù)組的簡單語法

寫 Swift 數(shù)組應該遵循像Array<SomeType>這樣的形式,其中SomeType是這個數(shù)組中唯一允許存在的數(shù)據(jù)類型。 我們也可以使用像[SomeType]這樣的簡單語法。 盡管兩種形式在功能上是一樣的,但是推薦較短的那種,而且在本文中都會使用這種形式來使用數(shù)組。

數(shù)組構造語句

我們可以使用字面量來進行數(shù)組構造,這是一種用一個或者多個數(shù)值構造數(shù)組的簡單方法。字面量是一系列由逗號分割并由方括號包含的數(shù)值。[value 1, value 2, value 3]

下面這個例子創(chuàng)建了一個叫做shoppingList并且存儲字符串的數(shù)組:

    var shoppingList: [String] = ["Eggs", "Milk"]
    // shoppingList 已經(jīng)被構造并且擁有兩個初始項。

shoppingList變量被聲明為“字符串值類型的數(shù)組“,記作[String]。 因為這個數(shù)組被規(guī)定只有String一種數(shù)據(jù)結構,所以只有String類型可以在其中被存取。 在這里,shoppinglist數(shù)組由兩個String值("Eggs""Milk")構造,并且由字面量定義。

注意:
Shoppinglist數(shù)組被聲明為變量(var關鍵字創(chuàng)建)而不是常量(let創(chuàng)建)是因為以后可能會有更多的數(shù)據(jù)項被插入其中。

在這個例子中,字面量僅僅包含兩個String值。匹配了該數(shù)組的變量聲明(只能包含String的數(shù)組),所以這個字面量的分配過程就是允許用兩個初始項來構造shoppinglist。

由于 Swift 的類型推斷機制,當我們用字面量構造只擁有相同類型值數(shù)組的時候,我們不必把數(shù)組的類型定義清楚。 shoppinglist的構造也可以這樣寫:

    var shoppingList = ["Eggs", "Milk"]

因為所有字面量中的值都是相同的類型,Swift 可以推斷出[String]shoppinglist中變量的正確類型。

訪問和修改數(shù)組

我們可以通過數(shù)組的方法和屬性來訪問和修改數(shù)組,或者下標語法。還可以使用數(shù)組的只讀屬性count來獲取數(shù)組中的數(shù)據(jù)項數(shù)量。

    println("The shopping list contains \(shoppingList.count) items.")
    // 輸出"The shopping list contains 2 items."(這個數(shù)組有2個項)

使用布爾項isEmpty來作為檢查count屬性的值是否為 0 的捷徑。

    if shoppingList.isEmpty {
        println("The shopping list is empty.")
    } else {
        println("The shopping list is not empty.")
    }
    // 打印 "The shopping list is not empty."(shoppinglist不是空的)

也可以使用append方法在數(shù)組后面添加新的數(shù)據(jù)項:

    shoppingList.append("Flour")
    // shoppingList 現(xiàn)在有3個數(shù)據(jù)項,有人在攤煎餅

除此之外,使用加法賦值運算符(+=)也可以直接在數(shù)組后面添加一個或多個擁有相同類型的數(shù)據(jù)項:

    shoppingList += ["Baking Powder"]
    // shoppingList 現(xiàn)在有四項了
    shoppingList += ["Chocolate Spread","Cheese","Butter"]
    // shoppingList 現(xiàn)在有七項了

可以直接使用下標語法來獲取數(shù)組中的數(shù)據(jù)項,把我們需要的數(shù)據(jù)項的索引值放在直接放在數(shù)組名稱的方括號中:

    var firstItem = shoppingList[0]
    // 第一項是 "Eggs"

注意第一項在數(shù)組中的索引值是0而不是1。 Swift 中的數(shù)組索引總是從零開始。

我們也可以用下標來改變某個已有索引值對應的數(shù)據(jù)值:

    shoppingList[0] = "Six eggs"
    // 其中的第一項現(xiàn)在是 "Six eggs" 而不是 "Eggs"

還可以利用下標來一次改變一系列數(shù)據(jù)值,即使新數(shù)據(jù)和原有數(shù)據(jù)的數(shù)量是不一樣的。下面的例子把"Chocolate Spread""Cheese",和"Butter"替換為"Bananas""Apples"

    shoppingList[4...6] = ["Bananas", "Apples"]
    // shoppingList 現(xiàn)在有六項

注意:
我們不能使用下標語法在數(shù)組尾部添加新項。如果我們試著用這種方法對索引越界的數(shù)據(jù)進行檢索或者設置新值的操作,我們會引發(fā)一個運行期錯誤。我們可以使用索引值和數(shù)組的count屬性進行比較來在使用某個索引之前先檢驗是否有效。除了當count等于 0 時(說明這是個空數(shù)組),最大索引值一直是count - 1,因為數(shù)組都是零起索引。

調(diào)用數(shù)組的insert(atIndex:)方法來在某個具體索引值之前添加數(shù)據(jù)項:

    shoppingList.insert("Maple Syrup", atIndex: 0)
    // shoppingList 現(xiàn)在有7項
    // "Maple Syrup" 現(xiàn)在是這個列表中的第一項

這次insert函數(shù)調(diào)用把值為"Maple Syrup"的新數(shù)據(jù)項插入列表的最開始位置,并且使用0作為索引值。

類似的我們可以使用removeAtIndex方法來移除數(shù)組中的某一項。這個方法把數(shù)組在特定索引值中存儲的數(shù)據(jù)項移除并且返回這個被移除的數(shù)據(jù)項(我們不需要的時候就可以無視它):

    let mapleSyrup = shoppingList.removeAtIndex(0)
    // 索引值為0的數(shù)據(jù)項被移除
    // shoppingList 現(xiàn)在只有6項,而且不包括Maple Syrup
    // mapleSyrup常量的值等于被移除數(shù)據(jù)項的值 "Maple Syrup"

數(shù)據(jù)項被移除后數(shù)組中的空出項會被自動填補,所以現(xiàn)在索引值為0的數(shù)據(jù)項的值再次等于"Six eggs":

    firstItem = shoppingList[0]
    // firstItem 現(xiàn)在等于 "Six eggs"

如果我們只想把數(shù)組中的最后一項移除,可以使用removeLast方法而不是removeAtIndex方法來避免我們需要獲取數(shù)組的count屬性。就像后者一樣,前者也會返回被移除的數(shù)據(jù)項:

    let apples = shoppingList.removeLast()
    // 數(shù)組的最后一項被移除了
    // shoppingList現(xiàn)在只有5項,不包括cheese
    // apples 常量的值現(xiàn)在等于"Apples" 字符串

數(shù)組的遍歷

我們可以使用for-in循環(huán)來遍歷所有數(shù)組中的數(shù)據(jù)項:

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

如果我們同時需要每個數(shù)據(jù)項的值和索引值,可以使用全局enumerate函數(shù)來進行數(shù)組遍歷。enumerate返回一個由每一個數(shù)據(jù)項索引值和數(shù)據(jù)值組成的元組。我們可以把這個元組分解成臨時常量或者變量來進行遍歷:

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

更多關于for-in循環(huán)的介紹請參見for 循環(huán)。

創(chuàng)建并且構造一個數(shù)組

我們可以使用構造語法來創(chuàng)建一個由特定數(shù)據(jù)類型構成的空數(shù)組:

    var someInts = [Int]()
    println("someInts is of type [Int] with \(someInts.count) items。")
    // 打印 "someInts is of type [Int] with 0 items。"(someInts是0數(shù)據(jù)項的Int[]數(shù)組)

注意someInts被設置為一個[Int]構造函數(shù)的輸出所以它的變量類型被定義為[Int]

除此之外,如果代碼上下文中提供了類型信息, 例如一個函數(shù)參數(shù)或者一個已經(jīng)定義好類型的常量或者變量,我們可以使用空數(shù)組語句創(chuàng)建一個空數(shù)組,它的寫法很簡單:[](一對空方括號):

    someInts.append(3)
    // someInts 現(xiàn)在包含一個INT值
    someInts = []
    // someInts 現(xiàn)在是空數(shù)組,但是仍然是[Int]類型的。

Swift 中的Array類型還提供一個可以創(chuàng)建特定大小并且所有數(shù)據(jù)都被默認的構造方法。我們可以把準備加入新數(shù)組的數(shù)據(jù)項數(shù)量(count)和適當類型的初始值(repeatedValue)傳入數(shù)組構造函數(shù):

    var threeDoubles = [Double](count: 3, repeatedValue:0.0)
    // threeDoubles 是一種 [Double]數(shù)組, 等于 [0.0, 0.0, 0.0]

因為類型推斷的存在,我們使用這種構造方法的時候不需要特別指定數(shù)組中存儲的數(shù)據(jù)類型,因為類型可以從默認值推斷出來:

    var anotherThreeDoubles = Array(count: 3, repeatedValue: 2.5)
    // anotherThreeDoubles is inferred as [Double], and equals [2.5, 2.5, 2.5]

最后,我們可以使用加法操作符(+)來組合兩種已存在的相同類型數(shù)組。新數(shù)組的數(shù)據(jù)類型會被從兩個數(shù)組的數(shù)據(jù)類型中推斷出來:

    var sixDoubles = threeDoubles + anotherThreeDoubles
    // sixDoubles 被推斷為 [Double], 等于 [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]

字典

字典是一種存儲多個相同類型的值的容器。每個值(value)都關聯(lián)唯一的鍵(key),鍵作為字典中的這個值數(shù)據(jù)的標識符。和數(shù)組中的數(shù)據(jù)項不同,字典中的數(shù)據(jù)項并沒有具體順序。我們在需要通過標識符(鍵)訪問數(shù)據(jù)的時候使用字典,這種方法很大程度上和我們在現(xiàn)實世界中使用字典查字義的方法一樣。

Swift 的字典使用時需要具體規(guī)定可以存儲鍵和值類型。不同于 Objective-C 的NSDictionaryNSMutableDictionary 類可以使用任何類型的對象來作鍵和值并且不提供任何關于這些對象的本質(zhì)信息。在 Swift 中,在某個特定字典中可以存儲的鍵和值必須提前定義清楚,方法是通過顯性類型標注或者類型推斷。

Swift 的字典使用Dictionary<KeyType, ValueType>定義,其中KeyType是字典中鍵的數(shù)據(jù)類型,ValueType是字典中對應于這些鍵所存儲值的數(shù)據(jù)類型。

KeyType的唯一限制就是可哈希的,這樣可以保證它是獨一無二的,所有的 Swift 基本類型(例如String,Int, DoubleBool)都是默認可哈希的,并且所有這些類型都可以在字典中當做鍵使用。未關聯(lián)值的枚舉成員(參見枚舉)也是默認可哈希的。

字典字面量

我們可以使用字典字面量來構造字典,它們和我們剛才介紹過的數(shù)組字面量擁有相似語法。一個字典字面量是一個定義擁有一個或者多個鍵值對的字典集合的簡單語句。

一個鍵值對是一個key和一個value的結合體。在字典字面量中,每一個鍵值對的鍵和值都由冒號分割。這些鍵值對構成一個列表,其中這些鍵值對由方括號包含并且由逗號分割:

    [key 1: value 1, key 2: value 2, key 3: value 3]

下面的例子創(chuàng)建了一個存儲國際機場名稱的字典。在這個字典中鍵是三個字母的國際航空運輸相關代碼,值是機場名稱:

    var airports: [String:String] = ["TYO": "Tokyo", "DUB": "Dublin"]

airports字典被定義為一種 [String: String],它意味著這個字典的鍵和值都是String類型。

注意:
airports字典被聲明為變量(用var關鍵字)而不是常量(let關鍵字)因為后來更多的機場信息會被添加到這個示例字典中。

airports字典使用字典字面量初始化,包含兩個鍵值對。第一對的鍵是TYO,值是Tokyo。第二對的鍵是DUB,值是Dublin。

這個字典語句包含了兩個String: String類型的鍵值對。它們對應airports變量聲明的類型(一個只有String鍵和String值的字典)所以這個字典字面量是構造兩個初始數(shù)據(jù)項的airport字典。

和數(shù)組一樣,如果我們使用字面量構造字典就不用把類型定義清楚。airports的也可以用這種方法簡短定義:

    var airports = ["TYO": "Tokyo", "DUB": "Dublin"]

因為這個語句中所有的鍵和值都分別是相同的數(shù)據(jù)類型,Swift 可以推斷出Dictionary<String, String>airports字典的正確類型。

讀取和修改字典

我們可以通過字典的方法和屬性來讀取和修改字典,或者使用下標語法。和數(shù)組一樣,我們可以通過字典的只讀屬性count來獲取某個字典的數(shù)據(jù)項數(shù)量:

    println("The dictionary of airports contains \(airports.count) items.")
    // 打印 "The dictionary of airports contains 2 items."(這個字典有兩個數(shù)據(jù)項)

可以使用布爾屬性isEmpty來快捷的檢查字典的count屬性是否等于0。

    if airports.isEmpty {
        println("The airports dictionary is empty.")
    } else {
        println("The airports dictionary is not empty.")
    }
    // 打印 "The airports dictionary is not empty.(這個字典不為空)"

我們也可以在字典中使用下標語法來添加新的數(shù)據(jù)項。可以使用一個合適類型的 key 作為下標索引,并且分配新的合適類型的值:

    airports["LHR"] = "London"
    // airports 字典現(xiàn)在有三個數(shù)據(jù)項

我們也可以使用下標語法來改變特定鍵對應的值:

    airports["LHR"] = "London Heathrow"
    // "LHR"對應的值 被改為 "London Heathrow

作為另一種下標方法,字典的updateValue(forKey:)方法可以設置或者更新特定鍵對應的值。就像上面所示的示例,updateValue(forKey:)方法在這個鍵不存在對應值的時候設置值或者在存在時更新已存在的值。和上面的下標方法不一樣,這個方法返回更新值之前的原值。這樣方便我們檢查更新是否成功。

updateValue(forKey:)函數(shù)會返回包含一個字典值類型的可選值。舉例來說:對于存儲String值的字典,這個函數(shù)會返回一個String?或者“可選 String”類型的值。如果值存在,則這個可選值值等于被替換的值,否則將會是nil。

    if let oldValue = airports.updateValue("Dublin Internation", forKey: "DUB") {
        println("The old value for DUB was \(oldValue).")
    }
    // 輸出 "The old value for DUB was Dublin."(DUB原值是dublin)

我們也可以使用下標語法來在字典中檢索特定鍵對應的值。由于使用一個沒有值的鍵這種情況是有可能發(fā)生的,可選類型返回這個鍵存在的相關值,否則就返回nil

    if let airportName = airports["DUB"] {
        println("The name of the airport is \(airportName).")
    } else {
        println("That airport is not in the airports dictionary.")
    }
    // 打印 "The name of the airport is Dublin Internation."(機場的名字是都柏林國際)

我們還可以使用下標語法來通過給某個鍵的對應值賦值為nil來從字典里移除一個鍵值對:

    airports["APL"] = "Apple Internation"
    // "Apple Internation"不是真的 APL機場, 刪除它
    airports["APL"] = nil
    // APL現(xiàn)在被移除了

另外,removeValueForKey方法也可以用來在字典中移除鍵值對。這個方法在鍵值對存在的情況下會移除該鍵值對并且返回被移除的value或者在沒有值的情況下返回nil

    if let removedValue = airports.removeValueForKey("DUB") {
        println("The removed airport's name is \(removedValue).")
    } else {
        println("The airports dictionary does not contain a value for DUB.")
    }
    // prints "The removed airport's name is Dublin International."

字典遍歷

我們可以使用for-in循環(huán)來遍歷某個字典中的鍵值對。每一個字典中的數(shù)據(jù)項都由(key, value)元組形式返回,并且我們可以使用臨時常量或者變量來分解這些元組:

    for (airportCode, airportName) in airports {
        println("\(airportCode): \(airportName)")
    }
    // TYO: Tokyo
    // LHR: London Heathrow

for-in循環(huán)請參見For 循環(huán)。

我們也可以通過訪問它的keys或者values屬性(都是可遍歷集合)檢索一個字典的鍵或者值:

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

    for airportName in airports.values {
        println("Airport name: \(airportName)")
    }
    // Airport name: Tokyo
    // Airport name: London Heathrow

如果我們只是需要使用某個字典的鍵集合或者值集合來作為某個接受Array實例 API 的參數(shù),可以直接使用keys或者values屬性直接構造一個新數(shù)組:

    let airportCodes = Array(airports.keys)
    // airportCodes is ["TYO", "LHR"]

    let airportNames = Array(airports.values)
    // airportNames is ["Tokyo", "London Heathrow"]

注意:
Swift 的字典類型是無序集合類型。其中字典鍵,值,鍵值對在遍歷的時候會重新排列,而且其中順序是不固定的。

創(chuàng)建一個空字典

我們可以像數(shù)組一樣使用構造語法創(chuàng)建一個空字典:

    var namesOfIntegers = Dictionary<Int, String>()
    // namesOfIntegers 是一個空的 Dictionary<Int, String>

這個例子創(chuàng)建了一個Int, String類型的空字典來儲存英語對整數(shù)的命名。它的鍵是Int型,值是String型。

如果上下文已經(jīng)提供了信息類型,我們可以使用空字典字面量來創(chuàng)建一個空字典,記作[:](中括號中放一個冒號):

    namesOfIntegers[16] = "sixteen"
    // namesOfIntegers 現(xiàn)在包含一個鍵值對
    namesOfIntegers = [:]
    // namesOfIntegers 又成為了一個 Int, String類型的空字典

注意:
在后臺,Swift 的數(shù)組和字典都是由泛型集合來實現(xiàn)的,想了解更多泛型和集合信息請參見泛型。

集合的可變性

數(shù)組和字典都是在單個集合中存儲可變值。如果我們創(chuàng)建一個數(shù)組或者字典并且把它分配成一個變量,這個集合將會是可變的。這意味著我們可以在創(chuàng)建之后添加更多或移除已存在的數(shù)據(jù)項來改變這個集合的大小。與此相反,如果我們把數(shù)組或字典分配成常量,那么它就是不可變的,它的大小不能被改變。

對字典來說,不可變性也意味著我們不能替換其中任何現(xiàn)有鍵所對應的值。不可變字典的內(nèi)容在被首次設定之后不能更改。不可變性對數(shù)組來說有一點不同,當然我們不能試著改變?nèi)魏尾豢勺償?shù)組的大小,但是我們可以重新設定相對現(xiàn)存索引所對應的值。這使得 Swift 數(shù)組在大小被固定的時候依然可以做的很棒。

Swift 數(shù)組的可變性行為同時影響了數(shù)組實例如何被分配和修改,想獲取更多信息,請參見集合在賦值和復制中的行為。

注意:
在我們不需要改變數(shù)組大小的時候創(chuàng)建不可變數(shù)組是很好的習慣。如此 Swift 編譯器可以優(yōu)化我們創(chuàng)建的集合。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號