Swift基础 集合类型

2023-07-17 17:10:20 浏览数 (1)

翻译自:https://docs.swift.org/swift-book/LanguageGuide/CollectionTypes.html

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

Swift 中的数组、集合和字典始终清楚可以存储的值和键的类型。这意味着您不能错误地将错误类型的值插入集合中。这也意味着您可以对从集合中检索的值类型有信心。

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

收藏品的可变性

如果您创建一个数组、集合或字典,并将其分配给变量,则创建的集合将是可变的。这意味着您可以在集合创建后通过添加、删除或更改集合中的项目来更改(或突变)。如果您将数组、集合或字典分配给常量,则该集合是不可变的,其大小和内容无法更改。

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

数组(Arrays)

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

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

数组类型速记语法

Swift数组的类型被完整写成Array<Element>,其中Element是允许数组存储的值类型。您还可以将数组的类型以速记形式写为[Element]虽然这两种形式在功能上相同,但速记形式是首选,并在参考数组类型时在整个指南中使用。

创建空数组

您可以使用初始化器语法创建特定类型的空数组:

代码语言:javascript复制
var someInts: [Int] = []
print("someInts is of type [Int] with (someInts.count) items.")
// Prints "someInts is of type [Int] with 0 items."

请注意,从初始化器的类型推断出someInts变量的类型为[Int]

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

代码语言:javascript复制
someInts.append(3)
// someInts now contains 1 value of type Int
someInts = []
// someInts is now an empty array, but is still of type [Int]

创建具有默认值的数组

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

代码语言:javascript复制
var threeDoubles = Array(repeating: 0.0, count: 3)
// threeDoubles is of type [Double], and equals [0.0, 0.0, 0.0]

通过将两个数组一起创建数组

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

代码语言:javascript复制
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]

使用数组字面创建数组

您还可以使用文字初始化数组,这是将一个或多个值写入数组集合的简写方式。数组文字写为值列表,用逗号分隔,周围是一对方括号:

[value 1, value 2, value 3]

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

代码语言:javascript复制
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的初始化可以以较短的形式编写:

代码语言:javascript复制
var shoppingList = ["Eggs", "Milk"]

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

访问和修改数组

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

要查找数组中的项目数量,请检查其只读count属性:

代码语言:javascript复制
print("The shopping list contains (shoppingList.count) items.")
// Prints "The shopping list contains 2 items."

使用布尔为isEmpty属性作为快捷方式,以检查count属性是否等于0

代码语言:javascript复制
if shoppingList.isEmpty {
   print("The shopping list is empty.")
} else {
   print("The shopping list isn't empty.")
}
// Prints "The shopping list isn't empty."

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

代码语言:javascript复制
shoppingList.append("Flour")
// shoppingList now contains 3 items, and someone is making pancakes

或者,将一个或多个兼容项的数组附加到加法赋值运算符( =

代码语言:javascript复制
shoppingList  = ["Baking Powder"]
// shoppingList now contains 4 items
shoppingList  = ["Chocolate Spread", "Cheese", "Butter"]
// shoppingList now contains 7 item

使用下标语法从数组中检索值,在数组名称后立即传递要在方括号内检索的值的索引:

代码语言:javascript复制
var firstItem = shoppingList[0]
// firstItem is equal to "Eggs"

注意 数组中的第一个项目的索引为0,而不是1。Swift中的数组总是零索引。

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

代码语言:javascript复制
shoppingList[0] = "Six eggs"
// the first item in the list is now equal to "Six eggs" rather than "Eggs"

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

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

代码语言:javascript复制
shoppingList[4...6] = ["Bananas", "Apples"]
// shoppingList now contains 6 items

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

代码语言:javascript复制
shoppingList.insert("Maple Syrup", at: 0)
// shoppingList now contains 7 items
// "Maple Syrup" is now the first item in the list

insert(_:at:)方法的调用在购物清单开头插入一个值为"MapleSyrup"的新项目,索引为0

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

代码语言:javascript复制
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属性进行比较,在使用索引之前检查索引是否有效。数组中最大的有效索引是count1,因为数组从零开始索引——然而,当count0(这意味着数组为空),没有有效的索引。

当删除项目时,数组中的任何间隙都会关闭,因此索引0的值再次等于"Sixeggs"

代码语言:javascript复制
firstItem = shoppingList[0]
// firstItem is now equal to "Six eggs"

如果你想从数组中删除最后一项,使用’ removeLast() ‘方法,而不是’ remove(at:) ‘方法,以避免需要查询数组的’ count ‘属性。像’ remove(at:) ‘方法一样,’ removeLast() ‘返回被删除的项:

代码语言:javascript复制
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

在数组上迭代

您可以使用for-in循环迭代数组中的整组值:

代码语言:javascript复制
for item in shoppingList {
   print(item)
}
// Six eggs
// Milk
// Flour
// Baking Powder
// Bananas

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

代码语言:javascript复制
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循环。

Sets

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

注意 Swift的Set类型与Foundation的NSSet类桥接。 For more information about using Set with Foundation and Cocoa, see Bridging Between Set and NSSet.

集合类型的哈希值

为了存储在集合中,类型必须是“哈希”的——也就是说,类型必须为自己提供一种计算“哈希值”的方法。哈希值是一个’ Int ‘值,它对所有相等比较的对象都是相同的,例如,如果’ A == b ‘, ‘ A ‘的哈希值等于’ b ‘的哈希值。

Swift的所有基本类型(如StringIntDoubleBool)默认都是可散列的,并且可以用作设置值类型或字典键类型。默认情况下,没有关联值的枚举案例值(如枚举中所述)也是可散列的。

注意 您可以使用自己的自定义类型作为设置值类型或字典键类型,使其符合Swift标准库中的Hashable协议。有关实现所需hash(into:)方法的信息,请参阅Hashable。有关遵守协议的信息,请参阅协议。

设置类型语法

Swift集的类型写为Set<Element>,其中Element是允许该集存储的类型。与数组不同,集合没有等效的速记形式。

创建和初始化空集

您可以使用初始化器语法创建特定类型的空集:

代码语言:javascript复制
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>

或者,如果上下文已经提供了类型信息,例如函数参数或已经键入的变量或常量,您可以创建一个带有空数组文字的空集:

代码语言:javascript复制
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>

使用数组字面创建集合

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

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

代码语言:javascript复制
var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
// favoriteGenres has been initialized with three initial items

favoritegenre 变量被声明为”一组 String 值”,写成 set <String> 。因为这个特定的集合指定了一个值类型 String ,它只允许存储 String 值。在这里, favoritegenre 集合被初始化为三个 String 值( RockClassicalHip hop ),写入一个数组文字。

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

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

代码语言:javascript复制
var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]

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

访问和修改一组

您通过其方法和属性访问和修改集合。

要了解集合中的项目数量,请检查其只读count属性:

代码语言:javascript复制
print("I have (favoriteGenres.count) favorite music genres.")
// Prints "I have 3 favorite music genres."

使用布尔为isEmpty属性作为快捷方式,以检查count属性是否等于0

代码语言:javascript复制
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(_:)方法将新项目添加到集合中:

代码语言:javascript复制
favoriteGenres.insert("Jazz")
// favoriteGenres now contains 4 items

您可以通过调用集合的remove(_:)方法从集合中删除项目,如果项目是集合的成员,则删除项目,并返回删除的值,如果集合不包含它,则返回nil。或者,集合中的所有项目都可以使用其removeAll()方法删除。

代码语言:javascript复制
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(_:)方法。

代码语言:javascript复制
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循环迭代集合中的值。

代码语言:javascript复制
for genre in favoriteGenres {
   print("(genre)")
}
// Classical
// Jazz
// Hip hop

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

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

代码语言:javascript复制
for genre in favoriteGenres.sorted() {
   print("(genre)")
}
// Classical
// Hip hop
// Jazz

执行集合操作

您可以高效地执行基本集合操作,例如将两个集合组合在一起,确定两个集合具有共同值,或确定两个集合是否包含所有、部分或不包含相同的值。

基本集操作

下面的插图描绘了两个集合——ab——以及由阴影区域表示的各种集合操作的结果。

  • 使用intersection(_:)方法创建一个新集合,其中只有两个集合共有的值。
  • 使用symmetricDifference(_:)方法创建一个新集合,其中两个集合都有值,但不是两者。
  • 使用union(_:)方法创建一个包含两个集合中所有值的新集合。
  • 使用subtracting(_:)方法创建一个值不在指定集合中的新集合。
代码语言:javascript复制
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]

设定成员资格和平等

下图描述了三个集合——a、b和c,其中重叠的区域表示集合之间共享的元素。设置一个是集的超集b,因为包含了所有元素b。相反地,b是集的一个子集,因为所有元素b也包含一组。b, c是不相交的,因为他们没有共同之处。

  • 使用“相等”运算符(==)确定两个集合是否包含所有相同的值。
  • Use the isSubset(of:) 方法确定一个集合的所有值是否包含在指定的集合中。
  • Use the isSuperset(of:) 方法确定一个集合是否包含指定集合中的所有值。
  • Use the isStrictSubset(of:) or isStrictSuperset(of:) 方法确定一个集合是子集还是超集,但不等于指定的集合。
  • 使用isDisjoint(with:)方法确定两个集合是否没有共同值。
代码语言:javascript复制
let houseAnimals: Set = ["


	

0 人点赞