Swift 中 Set 和 Array 的对比

前言

数组(Array) 和 集合(Set) 在一开始看起来似乎非常相似。它们都是集合类型,并且有很多共同之处。不过,在实际开发场景中,我们常常倾向于使用数组而非集合。虽然这不一定是个问题,但有时选择集合可能是一个更好的选择。

基础概念的区别

在探讨选择数组(Array)还是集合(Set)的实际原因之前,我们先来了解一下这两种类型在使用上的几个基本差异。

初始化方式

当我们对比数组和集合的初始化过程时,可以发现它们实际上是非常相似的。示例代码如下:

vbnet 复制代码
let array: [String] = ["Swift", "Objective-C", "Xcode", "Java", "C++"]
let set: Set<String> = ["Swift", "Objective-C", "Xcode", "Java", "C++"]

这两种类型都支持相同的 ExpressibleByArrayLiteral 协议,不过了解一下"数组字面量"类型默认就是数组这一点也是很有帮助的。也就是说,如果是相同的集合类型字符串字面量,不显式的指定类型的话默认就是数组类型。比如下面的代码:

c 复制代码
let array = ["Swift", "Objective-C", "Xcode", "Java", "C++"] // Array<String>

起初,你可能会认为这两个集合在初始化后是完全相同的。然而,实际情况并非如此。下面是来自官方文档的描述:

scala 复制代码
A set stores distinct values of the same type in a collection with no defined ordering. You can use a set instead of an array when the order of items is not important, or when you need to ensure that an item only appears once.

从这段文档中我们可以看出 Set 的与 Array 的两点非常重要的区别:

  • Set 存储的元素是无序的。
  • Set 存储的每个元素都是唯一的,没有重复的元素。

比如下面这段代码:

vbnet 复制代码
let set: Set<String> = ["Swift", "Objective-C", "Xcode", "Java", "C++", "Swift"] // ["Java", "C++", "Swift", "Xcode", "Objective-C"]

可以看到,虽然 set 里面存储了两个字面量为 Swift 的字符串,但打印中只包含一个,且打印的顺序和赋值的顺序并不一致。

在选择使用 Array 还是 Set 时,上述两点是非常重要,一定要考虑到。

添加元素

Array 和 Set 都是值类型。因此,如果使用 let 关键字将它们定义为常量,那么在尝试添加元素时就会出现以下错误:

c 复制代码
array.append("JS") // Cannot use mutating member on immutable value: 'array' is a 'let' constant
set.insert("JS") // Cannot use mutating member on immutable value: 'array' is a 'let' constant

如上述代码中所见,我们在此处发现了第一个不同之处。对于数组,我们需要使用 append(_:) 方法来添加元素,而对于 Set,则需要使用 insert(_:) 方法。这两种方法都会将 JS 字符串添加到集合中,但仅在数组中,我们确切地知道它已被添加到末尾,因为数组是有序排列的。

这里还有一个不同之处在于 insert(_:) 方法的返回类型。它会返回一个布尔值(表示插入是否成功)以及一个 memberAfterInsert 属性,该属性要么包含已存在的对象,要么包含刚刚插入的对象。如果想要向用户反馈某个对象是否已存在,这一点会很有用:

swift 复制代码
let result = set.insert("Swift")
if !result.inserted {
    print("\(result.memberAfterInsert) 已存在")
}

元素的唯一性

集合(Set)和数组(Array)之间的一个重要区别在于元素的唯一性。数组可以包含相同的值两次以及多次,而集合则绝不会出现重复元素。这也是上述的 insert(_:) 方法返回一个布尔值以表明插入操作是否真正成功的根本原因。

这是一个重要的区别,它可能是选择使用集合而非在数组上使用过滤器来剔除重复项的原因。

元素的有序

数组和集合之间的第二个区别在于元素的排列顺序。相关文档也有这样的描述:

  • 数组:一种有序的、可随机访问的集合。
  • 集合:一个不含重复元素的无序集合。

因此,如果你的使用场景需要考虑元素的顺序,那么应该选择使用数组。

性能差异

选择使用集合的另一个原因在于:当性能至关重要时,或者当需要处理大量数据时。集合中的元素必须符合 Hashable协议,这使得集合在性能方面具有优势。在小型集合和大型集合中查找元素所花费的时间是相同的。

一般来说,如果可能的话,创建不可变的集合对于提高性能来说是很有好处的。这适用于数组和集合这两种数据结构,并且能让 Swift 编译器优化创建的集合的性能。

总结

若需要元素有序,且可以存储重复元素选择 Array;若不需要元素有序,且元素值要唯一选择 Set。

相关推荐
杂雾无尘8 小时前
解密 Swift 5.5 中的 @MainActor, 深入了解其优势与误区
ios·swift·客户端
胡桃夹夹子21 小时前
xcode swift项目运行、连接真机运行报错,引入文件夹失败
cocoa·xcode·swift
卢叁1 天前
关于代码优化的一点思考
ios·swift
大熊猫侯佩2 天前
代码精讲:WWDC 25 @Animatable 宏 —— SwiftUI 动画的新突破
swiftui·swift·wwdc
大熊猫侯佩3 天前
SwiftUI Charts 入门:从零到一,笑谈“柱”状人生(二)
swiftui·swift·apple
大熊猫侯佩3 天前
SwiftUI Charts 入门:从零到一,笑谈“柱”状人生(三)
swiftui·swift·apple
大熊猫侯佩3 天前
SwiftUI Charts 入门:从零到一,笑谈“柱”状人生(一)
swiftui·swift·apple
归故里3 天前
[swift] 用泛型整合 Codable 和 文件读写!
swift
光影少年3 天前
js防抖、节流和扁平化实现
前端·javascript·掘金·金石计划
不会写代码的丝丽4 天前
iOS 队列与线程
swift