美文网首页
Swift编程五(集合类型)

Swift编程五(集合类型)

作者: 酒茶白开水 | 来源:发表于2019-06-19 16:56 被阅读0次

    案例代码下载

    集合类型

    Swift提供三种主要的集合类型,为数组,集合和字典,用于存储集合值。数组是有序的值集合。集合是唯一值的无序集合。字典是键值关联的无序集合。


    image

    Swift中的数组,集合和字典总是清楚它们可以存储的值和键的类型。这意味着您不能错误地将错误类型的值插入到集合中。这也意味着您可以对从集合中检索的值的类型充满信心。

    注意: Swift的数组,集合和字典类型实现为泛型集合。有关泛型类型和集合的更多信息,请参阅泛型。

    可变集合

    如果创建数组,集合或字典,并将其分配给变量,则创建的集合将是可变的。这意味着,你可以改变(或变异它是由添加,删除或改变创建后集合中的项目)的集合。如果将数组,集合或字典分配给常量,则该集合是不可变的,并且其大小和内容不能更改。

    注意: 在集合不需要更改的所有情况下,最好创建不可变集合。这样做可以使您更容易推理代码,并使Swift编译器能够优化您创建的集合的性能。

    数组

    一个阵列存储在有序列表中的相同类型的值。相同的值可以在不同位置多次出现在数组中。

    注意: Swift的Array类型被桥接到Foundation的NSArray类。有关Array与Foundation和Cocoa一起使用的更多信息,请参阅数组和NSArray之间的桥接。

    数组类型速记语法

    Swift数组的类型是完整写写法是Array<Element>,其中Element是允许数组存储的值的类型。您也可以用简写形式编写数组类型[Element]。虽然这两种形式在功能上是相同的,但是简写形式是优选的,并且在引用数组的类型时在本指南中使用。

    创建一个空数组

    您可以使用初始化语法创建某个类型的空数组:

    var someInts = [Int]()
    print("someInts is of type [Int] with \(someInts.count) items.")
    

    请注意,someInts变量的类型推断为[Int]来自初始化程序的类型。

    如果上下文已经提供了类型信息,例如函数参数或已经键入的变量或常量,则可以使用空数组文字创建一个空数组,该数组写为[](一对空方括号):

    someInts.append(3)
    someInts = []
    

    创建具有默认值的数组

    Swift的Array类型还提供了一个初始化器,用于创建一个特定大小的数组,并将其所有值设置为相同的默认值。您将此初始值设定项传递给相应类型(称为repeating)的默认值:以及在新数组(被调用count)中重复该值的次数:

    var threeDoubles = Array(repeating: 0.0, count: 3)
    // threeDoubles is of type [Double], and equals [0.0, 0.0, 0.0]
    

    通过添加两个数组来创建数组

    您可以通过使用加法运算符(+)将两个具有兼容类型的现有数组相加来创建新数组。新数组的类型是从您添加的两个数组的类型推断出来的:

    var 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]
    

    使用Array Literal创建数组

    您还可以使用数组文字初始化数组,这是将一个或多个值写为数组集合的简便方法。数组文字被写为值列表,用逗号分隔,由一对方括号括起来:[value 1, value 2, value 3]

    下面的示例创建一个名为shoppingList存储String值的数组:

    var shoppingList: [String] = ["Eggs", "Milk"]
    // shoppingList has been initialized with two initial items
    

    该shoppingList变量被声明为“字符串值数组”,写为[String]。由于此特定数组已指定值类型String,因此仅允许存储String值。这里,shoppingList数组初始化为两个String值("Eggs"和"Milk"),写在数组文字中。

    注意: 该shoppingList阵列被声明为一个变量(由var声明),而不是一个常数(用let声明因为更多的项目被添加到以下实例中的购物清单)。

    在这种情况下,数组文字包含两个String值,而不包含任何其他值。这匹配shoppingList变量声明的类型(只能包含String值的数组),因此允许使用数组文字的赋值作为初始化shoppingList两个初始项的方法。

    感谢Swift的类型推断,如果要使用包含相同类型值的数组文字对其进行初始化,则不必编写数组类型。初始化shoppingList可能是用较短的形式编写的:

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

    因为数组文字中的所有值都是相同的类型,所以Swift可以推断出这[String]是用于shoppingList变量的正确类型。

    访问和修改数组

    您可以通过其方法和属性或使用下标语法来访问和修改数组。

    要查找数组中的项数,请访问其只读属性count:

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

    使用Boolean值isEmpty属性作为检查count属性是否等于式0的快捷方:

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

    您可以通过调用数组的append(_:)方法将新项添加到数组的末尾:

    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"
    

    注意: 数组中的第一项具有索引0,而不是1。Swift中的数组始终为零索引。

    您可以使用下标语法更改给定索引处的现有值:

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

    使用下标语法时,您指定的索引必须有效。例如,写入尝试将项目附加到数组的末尾shoppingList[shoppingList.count] = "Salt"会导致运行时错误。

    您还可以使用下标语法一次更改值范围,即使替换值集的长度与要替换的范围不同。下面的示例替"Chocolate Spread"换,"Cheese"和"Butter"以及"Bananas"与"Apples":

    shoppingList[4...6] = ["Bananas", "Apples"]
    // shoppingList now contains 6 items
    

    要在指定索引处将项插入数组,请调用数组的insert(_:at:)方法:

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

    此insert(_:at:)方法调用会在购物清单的最开头插入一个新项目,其值由索引表示。"Maple Syrup"0

    同样,您使用该remove(at:)方法从数组中删除项。此方法删除指定索引处的项并返回已删除的项(尽管如果不需要,可以忽略返回的值):

    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
    

    注意: 如果尝试访问或修改数组现有边界之外的索引的值,则会触发运行时错误。通过将索引与数组的count属性进行比较,可以在使用之前检查索引是否有效。数组中最大的有效索引是count - 1因为数组从零开始索引 - 但是,当count是0(意味着数组为空)时,没有有效的索引。

    删除项时,数组中的任何间隙都会关闭,因此index 0处的值再次等于:"Six eggs"

    firstItem = shoppingList[0]
    // firstItem is now equal to "Six eggs"
    

    如果要从数组中删除最终项,请使用removeLast()方法而不是remove(at:)方法来避免查询数组的count属性。与remove(at:)方法一样,removeLast()返回已删除的项:

    let apples = shoppingList.removeLast()
    

    迭代数组

    可以用数组for-in循环遍历整个集合值:

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

    如果需要每个项的整数索引及其值,请使用该enumerated()方法迭代数组。对于数组中的每个项,该enumerated()方法返回由整数和项组成的元组。整数从零开始,每个项目计数加1; 如果枚举整个数组,则这些整数与项目的索引相匹配。您可以将元组分解为临时常量或变量,作为迭代的一部分:

    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
    

    有关for- in循环的更多信息,请参阅For-In循环。

    集合

    集合在集合中存储相同类型的不同值,没有定义的顺序。当项目的顺序不重要时,或者当您需要确保项目仅出现一次时,您可以使用集合而不是数组。

    注意: Swift的Set类型被桥接到Foundation的NSSet类。

    有关Set与Foundation和Cocoa一起使用的更多信息,请参阅Set和NSSet之间的桥接。

    集合类型的哈希值

    必须是可哈希化的类型才能存储在集合中 - 即,类型必须提供计算自身哈希值的方法。哈希值是Int值对于所有对象的相等比较的相同值,如果是a == b,则遵循a.hashValue == b.hashValue。

    所有Swift的基本类型(例如String,Int,Double,和Bool)默认情况下可哈希,并可以作为设定值类型或字典密键类型。默认情况下,没有关联值的枚举案例值(如枚举中所述)也是可哈希的。

    注意: 可以使用自己的自定义类型作为设置值类型或字典键类型,使它们符合Swift标准库中的Hashable协议。符合Hashable协议的类型必须提供一个Int名为gettable的属性hashValue。hashValue类型属性返回的值在同一程序的不同执行或不同程序中不需要相同。

    由于Hashable协议符合Equatable,符合类型还必须提供equals operator的实现。Equatable协议要求任何符合要求的实现是等价关系。也就是说,一个==实现必须满足以下三个条件,所有a,b以及c值:

    • a == a (自反)
    • a == b暗示(对称)b == a
    • a == b && b == c暗示(传递性)a == c

    有关符合协议的更多信息,请参阅协议。

    集合类型语法

    Swift集合类型写为Set<Element>,Element允许集存储的类型在哪里。与数组不同,集合没有等效的简写形式。

    创建和初始化空集

    可以使用初始化程序语法创建某个类型的空集:

    var letters = Set<Character>()
    print("letters is of type Set<Character> with \(letters.count) 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>
    

    使用Array Literal创建集合

    您还可以使用数组文字初始化集合,作为将一个或多个值写为集合集合的简写方式。

    下面的示例创建一个名为favoriteGenres存储String值的集合:

    var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
    // favoriteGenres has been initialized with three initial items
    

    favoriteGenres变量被声明为“一组String值”,写为Set<String>。由于此特定集已指定值类型String,因此仅允许存储String值。在此,favoriteGenres集合被初始化具有三个String值("Rock","Classical",和),阵列字面内写入。"Hip hop"

    注意: favoriteGenres集合被声明为变量(使用var声明)而不是常量(使用let声明),因为在下面的示例中添加和删除了项目。

    无法仅从数组文字中推断出集合类型,因此Set必须显式声明该类型。但是,由于Swift的类型推断,如果使用包含仅一种类型的值的数组文字初始化它,则不必编写集合元素的类型。初始化favoriteGenres是用较短的形式编写的:

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

    因为数组文字中的所有值都是相同的类型,所以Swift可以推断出这Set<String>是用于favoriteGenres变量的正确类型。

    访问和修改集

    可以通过其方法和属性访问和修改集。

    要查找集合中的项目数,请访问其只读属性count:

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

    使用Boolean类型的isEmpty属性作为检查count属性是否等于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."
    

    可以通过调用set的insert(_:)方法将新项添加到集合中:

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

    可以通过调用set的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."
    

    要检查集合是否包含特定项目,请使用该contains(_:)方法。

    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."
    

    遍历集合

    您可以使用for- in循环遍历集合中的值。

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

    有关for- in循环的更多信息,请参阅For-In循环。

    Swift的Set类型没有定义的顺序。要以特定顺序迭代集合的值,请使用该sorted()方法,该方法将集合的元素作为使用<运算符排序的数组返回。

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

    集合运算

    可以有效地执行基本集合操作,例如将两个集合组合在一起,确定两个集合具有哪些值,或者确定两个集合是否包含全部,一些或不包含相同的值。

    基本集合运算

    下图描绘了两个集a和b由阴影区域表示的各种运算操作的结果。


    image
    • 使用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()
    oddDigits.intersection(evenDigits).sorted()
    oddDigits.subtracting(singleDigitPrimeNumbers).sorted()
    oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted()
    

    集合会员资格和平等

    下图描绘了三个集合a,b和c具有表示在集合之间共享的元素的重叠区域。集合a是集合b的超集,因为a包含了所有元素b。相反,集合b是集合a的子集,因为b其中的所有元素也包含在a内。集合b和集合c彼此不相交,因为它们没有共同的元素。


    image
    • 使用“is equal”运算符(==)来确定两个集合是否包含所有相同的值。
    • 使用此isSubset(of:)方法确定集合的所有值是否都包含在指定的集合中。
    • 使用此isSuperset(of:)方法确定集合是否包含指定集合中的所有值。
    • 使用isStrictSubset(of:)或isStrictSuperset(of:)方法确定集合是否是指定集合的子集或超集,但不等于。
    • 使用该isDisjoint(with:)方法确定两个集合是否没有共同的值。
    let houseAnimals: Set = ["🐶", "🐱"]
    let farmAnimals: Set = ["🐮", "🐔", "🐑", "🐶", "🐱"]
    let cityAnimals: Set = ["🐦", "🐭"]
    houseAnimals.isSubset(of: farmAnimals)//ture
    farmAnimals.isSuperset(of: houseAnimals)//ture
    farmAnimals.isDisjoint(with: cityAnimals)//ture
    

    字典

    典存储相同类型的key和相同类型的值且没有定义排序之间的关联的集合。每个值都与唯一key相关联,唯一key充当字典中值的标识符。与数组中的项目不同,字典中的项目没有指定的顺序。当您需要根据标识符查找值时,可以使用字典,这与使用真实字典查找特定单词的定义的方式非常相似。

    注意: Swift的Dictionary类型被桥接到基金会的NSDictionary类。

    有关Dictionary与Foundation和Cocoa一起使用的更多信息,请参阅字典和NSDictionary之间的桥接。

    字典类型速记语法

    Swift字典Dictionary<Key, Value>的类型是完整的,Key是以用作字典键的值的类型,Value典】为这些键存储的值的类型。

    注意: 字典Key类型必须符合Hashable协议,如集合的值类型。

    您也可以用简写形式编写字典类型[Key: Value]。虽然这两种形式在功能上是相同的,但是简写形式是优选的,并且在本指南中在引用字典的类型时使用。

    创建一个空字典

    与数组一样,您可以Dictionary使用初始化程序语法创建某个类型的空:

    var namesOfIntegers = [Int: String]()
    

    此示例创建一个[Int: String]类型的空字典,以存储整数值的可读名称。它的键是Int类型,其值是String类型。

    如果上下文已经提供了类型信息,您可以创建一个空字典,其中包含一个空的字典文字,其写为[:](一对方括号内的冒号):

    namesOfIntegers[16] = "sixteen"
    namesOfIntegers = [:]
    

    使用Dictionary Literal创建字典

    您还可以使用字典文字初始化字典,字典文字与前面看到的数组文字具有相似的语法。字典文字是将一个或多个键值对编写为Dictionary集合的简便方法。

    键值对是一个键和值的组合。在字典文字中,每个键值对中的键和值由冒号分隔。键值对被写为列表,以逗号分隔,由一对方括号括起:[key 1: value 1, key 2: value 2, key 3: value 3]

    以下示例创建一个字典来存储国际机场的名称。在这个字典中,key是三个字母的国际航空运输协会代码,值是机场名称:

    var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
    

    airports字典被声明为[String: String]类型的,意思是Dictionary的key为String类型,并且其值也是String类型的。

    注意: airports字典被声明为一个变量(用var声明),而不是一个常数(用let声明),因为在下面的例子有更多的机场被添加到词典中。

    使用airports包含两个键值对的字典文字初始化字典。第一对有一个键值"YYZ"和值"Toronto Pearson"。第二对有一个键值"DUB"和值"Dublin"。

    这个字典文字包含两String: String对。airports键值类型匹配变量声明的类型(仅包含String键和String值的字典),因此允许分配字典文字作为使用两个初始项初始化airports字典的方法。

    与数组一样,如果要使用其键和值具有一致类型的字典文字对其进行初始化,则不必编写字典类型。初始化airports可能是用较短的形式编写的:

    var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
    

    因为文字中的所有键都是彼此相同的类型,并且同样所有值都是彼此相同的类型,Swift可以推断这是用于airports字典的正确类型是[String: String]。

    访问和修改字典

    可以通过其方法和属性或使用下标语法来访问和修改字典。

    与数组一样,可以通过检查只读属性count来查找Dictionary中的项目数:

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

    使用Boolean类型的isEmpty属性作为检查count属性是否等于0的快捷方式:

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

    可以使用下标语法将新项添加到字典中。使用适当类型的新键作为下标索引,并指定适当类型的新值:

    airports["LHR"] = "London"
    

    还可以使用下标语法来更改与特定键关联的值:

    airports["LHR"] = "London Heathrow"
    

    作为下标的替代方法updateValue(:forKey:),使用字典的方法来设置或更新特定键的值。与上面的下标示例一样,updateValue(:forKey:)如果键不存在,则该方法为设置值,或者如果该键已存在则更新值。但是,与下标不同,updateValue(_:forKey:)方法在执行更新后返回旧值。这可以检查是否进行了更新。

    updateValue(_:forKey:)方法返回字典值类型的可选值。例如,对于存储String值的字典,该方法返回type String?或“optional String”的值。如果在更新之前存在该键则此可选值包含该键的旧值,如果不存在任何键或则为nil:

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

    还可以使用下标语法从字典中检索特定键的值。因为可以请求不存在值的键,所以字典的下标返回字典值类型的可选值。如果字典包含所请求键的值,则下标返回包含该键的现有值的可选值。否则,返回nil:

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

    可以使用下标语法通过nil为该键指定值来从字典中删除键值对:

    airports["APL"] = "Apple International"
    airports["APL"] = nil
    

    使用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.")
    }
    

    遍历字典

    可以用字典遍历键值对for- in环。字典中的每个项都作为元组(key, value)返回,可以将元组的成员分解为临时常量或变量,作为迭代的一部分:

    for (airportCode, airportName) in airports {
        print("\(airportCode): \(airportName)")
    }
    

    有关for- in循环的更多信息,请参阅For-In循环。

    还可以通过访问字典的键keys和values属性来检索字典的键或值的可迭代集合:

    for airportCode in airports.keys {
        print("Airport code: \(airportCode)")
    }
    for airportName in airports.values {
        print("Airport name: \(airportName)")
    }
    

    如果需要API使用字典的键或值来创建Array实例,请使用keys或values属性初始化新数组:

    let airportCodes = [String](airports.keys)
    let airportNames = [String](airports.values)
    

    Swift的Dictionary类型没有定义顺序。要按特定顺序迭代字典的键或值,请在其keys或values属性上使用sorted()方法。

    相关文章

      网友评论

          本文标题:Swift编程五(集合类型)

          本文链接:https://www.haomeiwen.com/subject/yrwkqctx.html