当前位置: 首页 > news >正文

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 中我们有很多种定义它的方法:

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)类型的数据:

enum MixedValue {case int(Int)case string(String)
}let ary: [MixedValue] = [.int(1337), .string("Hello")]

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


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

Swift 基本功修炼:深入浅出 Swift 中的枚举类型


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

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

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

let ary: [Int] = [1337, 1338, 1339]let item = ary.first(where: { $0 == 1340 })

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

2. Set 其人

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

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

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 值相同,则老元素会保持不动,新元素则会被无情的“拒绝”。

class Item: Hashable, Equatable {let value: Intlet name: Stringstatic 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 = valueself.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 类型:

  • apple / swift-collections
    在这里插入图片描述

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

  • Swift 入门学习:集合(Collection)类型趣谈-上
  • Swift 入门学习:集合(Collection)类型趣谈-下

3. 考量运行性能

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

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

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

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

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

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


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

  • 你敢信!?几行代码让Swift数组初始化提速400多倍!
  • SwiftUI一招让List巨量数据刷新UI速度快100+倍
  • 有趣简单的M2处理器性能实验:Swift与C代码执行速度的比较
  • Swift忽略大小写搜索子字符串的三种方法及性能对比

4. Array 和 Set 选择原则

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

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

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

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

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

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


想要系统学习 Swift 语言的小伙伴们,千万不要错过我的《Swift 语言开发精讲》专栏哦,欢迎大家恣意观赏:

在这里插入图片描述

  • Swift 语言开发精讲

总结

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

感谢观赏,再会!😎

相关文章:

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

概述 初学 Swift 且头发茂密的小码农们在日常开发中必定会在数组&#xff08;Array&#xff09;和集合&#xff08;Set&#xff09;两种类型之间的选择中“摇摆不定”&#xff0c;这也是人之常情。 Array 和 Set 在某些方面“亲如兄弟”&#xff0c;但实际上它们之间却有着“云…...

C++ 类模板 函数模板

类模板 #include <bits/stdc.h> using namespace std; //多少变量就写多少个 template<typename T1, typename T2> class Cat { public:Cat(){}Cat(T1 name, T2 age){this->age age;this->name name;}void print(){cout << this->name << …...

OTP8脚-全自动擦鞋机WTN6020-低成本语音方案

一&#xff0c;产品开发背景 首先&#xff0c;随着人们生活质量的提升&#xff0c;对鞋子的保养需求也日益增加。鞋子作为人们日常穿着的重要组成部分&#xff0c;其清洁度和外观状态直接影响到个人形象和舒适度。因此&#xff0c;一种能够自动清洁和擦亮鞋子的设备应运而生&am…...

GpuMall智算云:meta-llama/llama3/Llama3-8B-Instruct-WebUI

LLaMA 模型的第三代&#xff0c;是 LLaMA 2 的一个更大和更强的版本。LLaMA 3 拥有 35 亿个参数&#xff0c;训练在更大的文本数据集上GpuMall智算云 | 省钱、好用、弹性。租GPU就上GpuMall,面向AI开发者的GPU云平台 Llama 3 的推出标志着 Meta 基于 Llama 2 架构推出了四个新…...

内存泄漏案例分享4-异步任务流内存泄漏

案例4——异步任务内存泄漏 异步任务&#xff0c;代指起子线程异步完成一些数据操作、网络接口请求等&#xff0c;通常会使用以下API&#xff1a; Runnbale&#xff0c;Thread,线程池RxJavaHandlerThread 而这些异步任务很有可能操作内存泄漏&#xff0c;下面我们以Rxjava为…...

【机器学习300问】100、怎么理解卷积神经网络CNN中的池化操作?

一、什么是池化&#xff1f; 卷积神经网络&#xff08;CNN&#xff09;中的池化&#xff08;Pooling&#xff09;操作是一种下采样技术&#xff0c;其目的是减少数据的空间维度&#xff08;宽度和高度&#xff09;&#xff0c;同时保持最重要的特征并降低计算复杂度。池化操作不…...

RPA机器人流程自动化如何优化人力资源工作流程

人力资源部门在支持员工和改善整体工作环节方面扮演着至关重要的角色&#xff0c;但是在人资管理的日常工作中&#xff0c;充斥着大量基于规则的重复性任务&#xff0c;例如简历筛选、面试安排、员工数据管理、培训管理、绩效管理等&#xff0c;这些任务通常需要工作人员花费大…...

OpenHarmony开发者大会2024:鸿心聚力 智引未来

2024年5月25日&#xff0c;OpenAtom OpenHarmony&#xff08;简称“OpenHarmony")委员会以“鸿心聚力&#xff0c;智引未来”为主题&#xff0c;在创新之城深圳举办OpenHarmony开发者大会2024&#xff0c;为开发者、产业组织、生态伙伴和行业客户搭建一个交流、分享和学习…...

新楚文化知网收录文学艺术类期刊投稿

《新楚文化》是由国家新闻出版总署批准&#xff0c;湖北省文学艺术界联合会主管&#xff0c;湖北今古传奇传媒集团有限公司主办的正规期刊。主要刊登文化、文学、艺术类稿件&#xff1b;包括传统文化、非遗、历史文化、地方文化、中外友好文化交流、文学作品研究、艺术研究等方…...

基于vue3速学angular

因为工作原因&#xff0c;需要接手新的项目&#xff0c;新的项目是angular框架的&#xff0c;自学下和vue3的区别&#xff0c;写篇博客记录下&#xff1a; 参考&#xff1a;https://zhuanlan.zhihu.com/p/546843290?utm_id0 1.结构上&#xff1a; vue3:一个vue文件&#xff…...

链游中的代币(Token)或加密货币(Cryptocurrency)是如何产生和使用的?

在区块链游戏&#xff08;链游&#xff09;中&#xff0c;代币和加密货币不仅是游戏经济的核心&#xff0c;也是连接现实世界与虚拟游戏世界的桥梁。这些数字货币不仅赋予了游戏内资产的真实价值&#xff0c;还为玩家提供了全新的互动和交易方式。下面&#xff0c;我们将深入探…...

2024年5月23日 (周四) 叶子游戏新闻

《Unclogged》Steam页面上线 马桶主题恐怖逃脱解谜Brody制作并发行&#xff0c;一款奇葩创意马桶主题恐怖逃脱解谜新游《Unclogged》Steam页面上线&#xff0c;本作暂不支持中文。 Meta人工智能主管杨立昆 大语言模型不会达到人类智能水平IT之家今日&#xff08;5月23日&#x…...

猫毛过敏终结者!宠物空气净化器让你告别红眼和喷嚏

猫毛过敏是一种常见的过敏性疾病&#xff0c;影响着全球数百万人的日常生活。这种过敏反应通常是由于对猫皮屑、唾液或尿液中的蛋白质产生免疫反应而引起的。症状可能包括打喷嚏、流鼻涕、眼睛痒、皮肤疹和呼吸困难&#xff0c;严重影响患者的舒适度和生活质量。对于猫毛过敏者…...

xgboost项目实战-保险赔偿额预测与信用卡评分预测001

目录 算法代码 原理 算法流程 xgb.train中的参数介绍 params min_child_weight gamma 技巧 算法代码 代码获取方式&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1QV7nMC5ds5wSh-M9kuiwew?pwdx48l 提取码&#xff1a;x48l 特征直方图统计&#xff1a; fig, …...

子网划分,交换机原理与配置

子网划分 IP地址 IPv4由32位二进制数组成&#xff0c;一般用点分十进制来表示 IPv4是由32位二进制数组成&#xff0c;分成四组,第组八位。例如:11000000.10101000.00000000.00000010 为了便于配置通常表示成点分十进制形式例如:192.168.0.2 255.255.255.0 IPv6由128位组成&…...

记mapboxGL实现鼠标经过高亮时的一个问题

概述 mapboxGL实现鼠标经过高亮可通过注册图层的mousemove和moveout事件来实现&#xff0c;在mousemove事件中可以拿到当前经过的要素&#xff0c;但是当使用该要素时&#xff0c;发现在某个地图级别下会有线和面数据展示不全的情况。究其原因&#xff0c;发现是mapboxGL在绘图…...

AI重塑了我的工作流

阅读内容 Inhai: Agentic Workflow&#xff1a;AI 重塑了我的工作流 4 种主要的 Agentic Workflow 设计模式 Reflection&#xff08;反思&#xff09;&#xff1a;让 Agent 审视和修正自己生成的输出。 举例&#xff1a;如果有两个 Agent&#xff1a;一个负责 Coding&#…...

vue使用Less报错semi-colon expectedcss(css-semicolonexpected)的解决方法

1、将 styleint 依赖项添加到项目中 npm install --save-dev stylelint stylelint-config-standard2、在根目录中添加stylelint.config.js文件&#xff08;与package.json同级&#xff09; module.exports {extends: ["stylelint-config-standard"],rules: {"…...

如何使用golang自带工具对代码进行覆盖率测试

在 Go 语言中&#xff0c;测试代码覆盖率通常使用 go test 命令结合 -cover 和 -coverprofile 1. 基本代码覆盖率报告 在项目目录下运行以下命令 go test -cover这将在控制台输出一个代码覆盖率的百分比。但是&#xff0c;这种方式不会保存覆盖率数据&#xff08;可以指定目…...

Android studio版本和Android gradle plugin版本对应表

1.Android studio 版本的升级&#xff0c;一个方面上看主要是升级对AGP最高版本的支持 2.那为什么AGP要出高版本呢&#xff0c;主要支持高版本的API&#xff0c;真是一环扣一环...

遍历 Map 类型集合的方法汇总

1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

CMake基础:构建流程详解

目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端

&#x1f31f; 什么是 MCP&#xff1f; 模型控制协议 (MCP) 是一种创新的协议&#xff0c;旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议&#xff0c;它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

反射获取方法和属性

Java反射获取方法 在Java中&#xff0c;反射&#xff08;Reflection&#xff09;是一种强大的机制&#xff0c;允许程序在运行时访问和操作类的内部属性和方法。通过反射&#xff0c;可以动态地创建对象、调用方法、改变属性值&#xff0c;这在很多Java框架中如Spring和Hiberna…...

dify打造数据可视化图表

一、概述 在日常工作和学习中&#xff0c;我们经常需要和数据打交道。无论是分析报告、项目展示&#xff0c;还是简单的数据洞察&#xff0c;一个清晰直观的图表&#xff0c;往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server&#xff0c;由蚂蚁集团 AntV 团队…...

招商蛇口 | 执笔CID,启幕低密生活新境

作为中国城市生长的力量&#xff0c;招商蛇口以“美好生活承载者”为使命&#xff0c;深耕全球111座城市&#xff0c;以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子&#xff0c;招商蛇口始终与城市发展同频共振&#xff0c;以建筑诠释对土地与生活的…...

Mysql8 忘记密码重置,以及问题解决

1.使用免密登录 找到配置MySQL文件&#xff0c;我的文件路径是/etc/mysql/my.cnf&#xff0c;有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...

【JavaSE】多线程基础学习笔记

多线程基础 -线程相关概念 程序&#xff08;Program&#xff09; 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序&#xff0c;比如我们使用QQ&#xff0c;就启动了一个进程&#xff0c;操作系统就会为该进程分配内存…...

sshd代码修改banner

sshd服务连接之后会收到字符串&#xff1a; SSH-2.0-OpenSSH_9.5 容易被hacker识别此服务为sshd服务。 是否可以通过修改此banner达到让人无法识别此服务的目的呢&#xff1f; 不能。因为这是写的SSH的协议中的。 也就是协议规定了banner必须这么写。 SSH- 开头&#xff0c…...

echarts使用graphic强行给图增加一个边框(边框根据自己的图形大小设置)- 适用于无法使用dom的样式

pdf-lib https://blog.csdn.net/Shi_haoliu/article/details/148157624?spm1001.2014.3001.5501 为了完成在pdf中导出echarts图&#xff0c;如果边框加在dom上面&#xff0c;pdf-lib导出svg的时候并不会导出边框&#xff0c;所以只能在echarts图上面加边框 grid的边框是在图里…...