Swift 初学者交心:在 Array 和 Set 之间我们该如何抉择?

概述

初学 Swift 且头发茂密的小码农们在日常开发中必定会在数组(Array)和集合(Set)两种类型之间的选择中"摇摆不定",这也是人之常情。

Array 和 Set 在某些方面"亲如兄弟",但实际上它们之间却有着"云泥之别"。

在本篇博文中,您将学到如下内容:

  1. Array 其人
  2. Set 其人
  3. 考量运行性能
  4. Array 和 Set 选择原则

相信学完本课后,小伙伴们定能在开发中对如何选择 Array 和 Set 两种数据类型胸有成竹。

闲言少叙,Let's make a choice!!!;)


1. Array 其人

Array 俗称数组,它实际上就是一个遵守 Collection 协议的泛型结构。在 Swift 中我们有很多种定义它的方法:

swift 复制代码
var ary0 = Array<Int>()
var ary1 = [Int]()
var ary2: [Int] = []

let ary3 = [Int](repeating: 0, count: 100)
let ary4 = [1,2,3,4,5]
let ary5 = Array((0..<100))

如上代码所示,我们还可以通过 let 和 var 关键字来调整数组的可变性。

一般来说,Swift 数组中的元素类型都是同构(或同质,homogeneous)的,这可以从它定义中只有一个泛型元素类型参数来得到印证。


这里不考虑使用 AnyObject 或 Any 作为数组的元素类型,也不考虑使用 Swift 5.7 之后的 any ProtocolType 来"打包存放"遵循相同协议的不同元素类型。


不过通过巧妙的利用枚举,我们可以间接的向其内部"注入"异构(heterogeneous)类型的数据:

swift 复制代码
enum MixedValue {
  case int(Int)
  case string(String)
}

let ary: [MixedValue] = [.int(1337), .string("Hello")]

如上代码所示:虽然表面上看数组 ary 中放入了 Int 和 String 两种不同类型的数据,但其实它们都是同一种 MixedValue 枚举类型。


关于更多 Swift 中枚举类型的介绍,请小伙伴们恣意观赏下面的视频课:

video(video-MUNKNMJa-1716794478660)(type-csdn)(url-[live.csdn.net/v/embed/340...](https://link.juejin.cn?target=https%3A%2F%2Flive.csdn.net%2Fv%2Fembed%2F340074)(image-https%3A%2F%2Fi-blog.csdnimg.cn%2Fblog_migrate%2F1e379783b821bc3a06c9433a52559afa.png)(title-Swift "https://live.csdn.net/v/embed/340074)(image-https://i-blog.csdnimg.cn/blog_migrate/1e379783b821bc3a06c9433a52559afa.png)(title-Swift") 基本功修炼:深入浅出 Swift 中的枚举类型)


Array 中元素的存放是有顺序的,无论我们遍历数组 1 千遍或是 1 万遍它们的顺序都是绝对不会变的。

除此之外,数组中的元素是可以相同的,没有哪条规定说数组不能放入一样的东西进去。这也是为什么数组定义中其元素(Element)类型是没有任何限制条件的。

Array 本身为我们提供了丰富的方法可供调用,其中最常用的一个恐怕得算是 first(where:) 方法了,它被用来在数组中查找符合条件的元素:

swift 复制代码
let ary: [Int] = [1337, 1338, 1339]

let item = ary.first(where: { $0 == 1340 })

需要注意的是:first(where:) 方法可能返回 nil 值哦。

2. Set 其人

简单聊完了数组之后,接下来我们再来看看 Set 类型。

Set 同样是一种保存同质元素的容器,我们也可以用多种方式来初始化它的实例:

swift 复制代码
let set0 = Set<Int> ([1, 2, 2, 3, 3, 4, 4, 5, 5]) 
print (set0)

var set1: Set<String> = []
set1.insert ("Hopy") 
set1.insert ("Hopy") 
set1.insert ("Panda" )
print (set1)

从上面代码来看 Set 和 Array 很相似,不过它们却有一个关键的不同:Set 中的元素必须遵守 Hashable 协议

这意味着:不遵循 Hashable 的类型不能放到 Set 中去,这在我们创建自定义类型时尤其需要注意。

遵循 Hashable 协议带来的另一个重要限制是:Set 中的元素必须拥有不同的 Hash 值才行。如果插入 Set 的新元素和 Set 已有元素的 Hash 值相同,则老元素会保持不动,而新元素会被无情的"拒绝"。

swift 复制代码
class Item: Hashable, Equatable {
    let value: Int
    let name: String
    
    static func ==(lhs: Item, rhs: Item) -> Bool {
        lhs.value == rhs.value
    }
    
    func hash(into hasher: inout Hasher) {
        hasher.combine(value)
    }
    
    init(value: Int, name: String) {
        self.value = value
        self.name = name
    }
    
    deinit {
        print("\(name) deinited!")
    }
}

var mySet = Set<Item>()

func test() {
    let item_0 = Item(value: 1, name: "hopy")
    let item_1 = Item(value: 1, name: "大熊猫侯佩")
    
    mySet.insert(item_0)
    mySet.insert(item_1)
}

test()
print(mySet.first?.name)

以上代码运行结果如下所示:

我们可以用 contains() 方法来检查 Set 是否包含指定的元素,我们同样可以用与数组类似的 first(where:) 方法来查找集合中符合条件的元素。

Set 和数组另一个非常重要的区别是:Set 是无序的。

当我们多次遍历同一个 Set 时即可发现:其内部元素的顺序是无法保持稳定的。

如果大家希望兼顾 Array 和 Set 两者的优点:有序且唯一,我们可以使用 Apple 官方 swift-collections package 库中的 OrderedSet 类型:


更多 Swift 语言中集合类型的介绍,请感兴趣的小伙伴们移步如下博文观赏进一步精彩的内容:


3. 考量运行性能

现在,我们已经分别简单介绍了 Swift 中的 Array 和 Set 两种类型,不过它们到底"孰是孰非",我们还是无所适从。

接下来我们就从两者运行性能的角度来分析一番吧。

分析数组和集合性能的重要因素是它们内部元素遍历的速度。

数组元素遍历性能是 O(n),这意味着在最坏的情况下我们需要找遍数组中的所有元素才能得偿所愿,这在数组包含海量元素时会变得"蜗行牛步"。

反过来集合元素遍历性能却是 O(1),这说明要捕获 Set 内部任意元素所花费的时间都是一个"常数",不管它们的数量是 100 个或是 1 亿个。

通过上面的讨论,我们清楚可知一点:如果我们的目的就是用最快的速度从一大堆对象里查找心仪的元素,那么 Set 是当仁不让的选择。


包含海量元素的数组在使用中往往是性能的"阿格琉斯之踵",如果对此感兴趣的小伙伴们可以移步到以下博文中进一步学习性能优化之道:


4. Array 和 Set 选择原则

综上所述,我们认为在 Set 和 Array 之间选择的最佳决策是基于实际撸码的"情境"。

当我们拥有一个哈希项目(Item)列表,这些项目在一个集合中需要是唯一的,且无需排序,这时我们倾向于使用 Set。

如果你关心秩序,或者你无法使 Item 遵守 Hashable 协议(这貌似不大可能),那么我们更应考虑使用数组。

当然,也有一个例外:即我们可能希望在维持元素顺序稳定时同时保证唯一性。在这种情况下,我们可以选择使用 swift-collections 框架中的OrderedSet(有序集合)类型。

最后一个非常重要的撸码要诀是:我们的决定总是优先基于以上原则,而不是首先考虑性能之类的东东。除非我们正在编写一段性能非常关键的代码,而在该代码中有足够的证据来证明 Set 和 Array 间的性能优劣。

记住:过早优化是万恶之源!切记!切记!

总结

在本篇博文中,我们简单介绍了 Swift 语言中数组(Array)和集合(Set)两种类型的特性,并比较了它们在实际运行中的性能,最后我们进一步探讨了选择它们的基本原则。

感谢观赏,再会!8-)

相关推荐
国科安芯4 分钟前
【AS32系列MCU调试教程】性能优化:Eclipse环境下AS32芯片调试效率提升
java·性能优化·eclipse
徐新帅9 分钟前
基于 C 语言的图书管理系统开发详解
c语言·开发语言·数据结构
勇闯IT37 分钟前
有多少小于当前数字的数字
java·数据结构·算法
এ᭄画画的北北1 小时前
力扣-279.完全平方数
数据结构·算法·leetcode
GalaxyPokemon2 小时前
LeetCode - 69. x 的平方根
java·数据结构·算法
雨果talk3 小时前
Spring Boot集成Mina的Socket资源管理:从稳定通信到高性能优化
spring boot·后端·性能优化
晴空闲雲6 小时前
数据结构与算法-线性表-线性表的应用
数据结构
wangjialelele7 小时前
双向链表——(有头双向循环链表)
数据结构·链表
梦境虽美,却不长7 小时前
数据结构 学习 链表 2025年6月14日08点01分
数据结构·学习·链表
无聊的小坏坏8 小时前
一文详解前缀和:从一维到二维的高效算法应用
数据结构·算法