Swift-集合(Sets)

来源:互联网 发布:c语言数组比大小 编辑:程序博客网 时间:2024/06/06 01:05
集合(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(传递性)
集合类型语法
Swift中的Set类型被写为Set<Element>,这里的Element表示Set中允许存储的类型,和数组不同的是,集合没有等价的简化形式。

创建和构造一个空的集合
你可以通过构造器语法创建一个特定类型的空集合:

var letters = Set<Character>()print("letters is of type Set<Character> with \(letters.count) items.")// 打印 "letters is of type Set<Character> with 0 items."
注意:通过构造器,这里的letters变量的类型被推断为Set<Character>。
此外,如果上下文提供了类型信息,比如作为函数的参数或者已知类型的变量或常量,我们可以通过一个空的数组字面量创建一个空的Set:

letters.insert("a")// letters 现在含有1个 Character 类型的值letters = []// letters 现在是一个空的 Set, 但是它依然是 Set<Character> 类型

用数组字面量创建集合
你可以使用数组字面量来构造集合,并且可以使用简化形式写一个或者多个值作为集合元素。
下面的例子创建一个称之为favoriteGenres的集合来存储String类型的值:

var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]// favoriteGenres 被构造成含有三个初始值的集合
这个favoriteGenres变量被声明为“一个String值的集合”,写为Set<String>。由于这个特定的集合含有指定String类型的值,所以它只允许存储String类型值。这里的favoriteGenres变量有三个String类型的初始值("Rock","Classical"和"Hip hop"),并以数组字面量的方式出现。

注意:favoriteGenres被声明为一个变量(拥有var标示符)而不是一个常量(拥有let标示符),因为它里面的元素将会在下面的例子中被增加或者移除。

一个Set类型不能从数组字面量中被单独推断出来,因此Set类型必须显式声明。然而,由于 Swift 的类型推断功能,如果你想使用一个数组字面量构造一个Set并且该数组字面量中的所有元素类型相同,那么你无须写出Set的具体类型。favoriteGenres的构造形式可以采用简化的方式代替:

var favoriteGenres1: Set = ["Rock", "Classical", "Hip hop"]//由于数组字面量中的所有元素类型相同,Swift 可以推断出Set<String>作为favoriteGenres变量的正确类型。

访问和修改一个集合
你可以通过Set的属性和方法来访问和修改一个Set。
为了找出一个Set中元素的数量,可以使用其只读属性count:

print("I have \(favoriteGenres.count) favorite music genres.")// 打印 "I have 3 favorite music genres."
使用布尔属性isEmpty作为一个缩写形式去检查count属性是否为0:

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

你可以通过调用Setinsert(_:)方法来添加一个新元素:

favoriteGenres.insert("Jazz")// favoriteGenres 现在包含4个元素
你可以通过调用Set的remove(_:)方法去删除一个元素,如果该值是该Set的一个元素则删除该元素并且返回被删除的元素值,否则如果该Set不包含该值,则返回nil。另外,Set中的所有元素可以通过它的removeAll()方法删除。

if let removedGenre = favoriteGenres.remove("Rock") {    print("\(removedGenre)? I'm over it.")} else {    print("I never much cared for that.")}// 打印 "Rock? I'm over it."
使用contains(_:)方法去检查Set中是否包含一个特定的值:

if favoriteGenres.contains("Funk") {    print("I get up on the good foot.")} else {    print("It's too funky in here.")}// 打印 "It's too funky in here."
遍历一个集合
你可以在一个for-in循环中遍历一个Set中的所有值。

for genre in favoriteGenres {    print("\(genre)")}// Classical// Jazz// Hip hop
Swift 的Set类型没有确定的顺序,为了按照特定顺序来遍历一个Set中的值可以使用sorted()方法,它将返回一个有序数组,这个数组的元素排列顺序由操作符'<'对元素进行比较的结果来确定.

for genre in favoriteGenres.sorted() {    print("\(genre)")}// prints "Classical"// prints "Hip hop"// prints "Jazz
集合操作
你可以高效地完成Set的一些基本操作,比如把两个集合组合到一起,判断两个集合共有元素,或者判断两个集合是否全包含,部分包含或者不相交。

基本集合操作
下面的插图描述了两个集合-a和b-以及通过阴影部分的区域显示集合各种操作的结果。

1 使用intersection(_:)方法根据两个集合中都包含的值创建的一个新的集合。
2 使用symmetricDifference(_:)方法根据在一个集合中但不在两个集合中的值创建一个新的集合。
3 使用union(_:)方法根据两个集合的值创建一个新的集合。
4 使用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]
集合成员关系和相等
下面的插图描述了三个集合-a,b和c,以及通过重叠区域表述集合间共享的元素。集合a是集合b的父集合,因为a包含了b中所有的元素,相反的,集合b是集合a的子集合,因为属于b的元素也被a包含。集合b和集合c彼此不关联,因为它们之间没有共同的元素。
1 使用“是否相等”运算符(==)来判断两个集合是否包含全部相同的值。
2 使用isSubset(of:)方法来判断一个集合中的值是否也被包含在另外一个集合中。
3 使用isSuperset(of:)方法来判断一个集合中包含另一个集合中所有的值。
4 使用isStrictSubset(of:)或者isStrictSuperset(of:)方法来判断一个集合是否是另外一个集合的子集合或者。父集合并且两个集合并不相等。
5 使用isDisjoint(with:)方法来判断两个集合是否不含有相同的值(是否没有交集)。

let a: Set = ["1","2","3","4","5","6"]let b: Set = ["1","2"]let c: Set = ["7"]b.isSubset(of: a)  // truea.isSuperset(of: b) // truea.isDisjoint(with: c) // true

Set扩展
1 集合唯一和有序性
为Sequence添加一个扩展,用于获取Sequence中所有唯一的元素。因为我们是很容易将所有的元素放到Set中,并且返回内容,但是这并不稳定,因为Set的顺序是未定义的,为了保证输入元素的顺序和唯一性。进行如下扩展

extension Sequence where Iterator.Element: Hashable {    func unique() -> [Iterator.Element] {        var seen: Set<Iterator.Element> = []        return filter({            if seen.contains($0) {               return false            }else {               seen.insert($0)                return true            }        })    }}[1,2,3,12,1,3,4,5,6,4,6].unique() // [1, 2, 3, 12, 4, 5, 6]






0 0
原创粉丝点击