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。

相关推荐
YungFan9 小时前
iOS26适配指南之UIViewController
ios·swift
冯志浩3 天前
React Native 中 useEffect 的使用
react native·掘金·金石计划
公众号_醉鱼Java3 天前
Elasticsearch 字段膨胀使用 Flattened类型
后端·掘金·金石计划
HarderCoder3 天前
我们真的需要 typealias 吗?——一次 Swift 抽象成本的深度剖析
swift
HarderCoder3 天前
ByAI-Swift 6 全览:一份面向实战开发者的新特性速查手册
swift
HarderCoder3 天前
Swift 中 let 与 var 的真正区别:不仅关乎“可变”与否
swift
HarderCoder3 天前
深入理解 Swift 6.2 并发:从默认隔离到@concurrent 的完整指南
swift
麦兜*4 天前
Swift + Xcode 开发环境搭建终极指南
开发语言·ios·swiftui·xcode·swift·苹果vision pro·swift5.6.3
HarderCoder6 天前
Swift Concurrency:彻底告别“线程思维”,拥抱 Task 的世界
swift
HarderCoder6 天前
深入理解 Swift 中的 async/await:告别回调地狱,拥抱结构化并发
swift