Swift 5.1 (4) - 集合类型

级别: ★☆☆☆☆
标签:「iOS」「Swift 5.1」「Set」「Array」「Dictionary」
作者: 沐灵洛
审校: QiShare团队


集合类型

Swift提供三种主要的集合类型,称为ArraySetDictionary,用于存储值的集合。Array是有序的值的集合。Set是唯一值的无序集合。Dictionary是键值关联的无序集合。
可变集合:
如果创建数组,集合或字典,并将其分配给变量,则创建的集合将是可变的。我们可以通过添加,删除或更改集合中的项目来更改集合。如果将数组,集合或字典分配给常量,则该集合是不可变的,并且其大小和内容不能更改。
数组:
数组可以存储相同类型的值,并且相同的值可以在数组不同位置多次出现。

  1. 数组类型的简写语法:a. Array <Element> b. [Element]其中Element是允许数组存储的值的类型。其中[Element]简写形式优选。
  2. 创建一个空的数组:
    使用初始化语法创建某个类型的空数组:
//方式一
let intArray = Array<Int>()
print("intArray中有\(intArray.count)个元素")
//方式二
let intArray = [Int]()
print("intArray中有\(intArray.count)个元素")

如果上下文已经提供了数组中的类型信息,则数组存储的值得类型就是确定的。

  1. 创建带有初始值得数组:
    Swift的Array类型提供了一个初始化方法,用于创建一个特定大小的数组,并将其所有值设置为相同的默认值。
let defaultValueArray = Array.init(repeating: "你好", count: 3)
print(defaultValueArray)//!< ["你好", "你好", "你好"]
  1. 两个数组相加:
    使用加法运算符+将两个具有兼容类型的现有数组相加来创建新数组。新数组的类型是从我们添加的两个数组的类型推断出来的。这两个相加的数组的类型必须要可以兼容,否则编译器会报错。
let defaultValueArray = Array.init(repeating: "你好", count: 3)
let secondValueArray = Array.init(repeating: "大佬", count: 2)
let thirdValueArray = defaultValueArray + secondValueArray
print(thirdValueArray)//!< ["你好", "你好", "你好", "大佬", "大佬"]
  1. 使用字面量元素初始化数组:
    使用数组字面量元素初始化数组,语法:[value 1, value 2, value 3]
let literalArray = ["逗哥","刀哥"] //!< ["逗哥", "刀哥"] 有初值,可以类型推断,不必声明数组的类型
let literalArray1 : [Int] = [6,7]//!< [6, 7] 声明了类型
  1. 数组的访问和修改:
    count属性:访问数组元素的个数。isEmpty属性值Bool类型数组判空,相较Array.count == 0更高效。
if intArray.isEmpty {
   print("intArray:\(intArray)中有\(intArray.count)个元素")//!< intArray:[]中有0个元素
}

使用append(_:)方法拼接新项到数组的末尾

var appendArray = ["飞哥","强哥"]
appendArray.append("大哥")//!< ["飞哥", "强哥", "大哥"]

使用加法赋值运算符+=追加一个或多个兼容项的数组:

var appendArray = ["飞哥","强哥"]
let array1 = ["finally","ok"]
let array2 = ["拯救","静","\u{65}"]
appendArray += array1 //!< ["飞哥", "强哥", "finally", "ok"]
appendArray += array2 //!< ["飞哥", "强哥", "finally", "ok", "拯救", "静", "e"]

使用下标从数组中取值:

let literalArray = ["逗哥","刀哥"] //!< ["逗哥", "刀哥"]
let firstStr = literalArray[0]
print(firstStr) //!< 逗哥

使用下标更改给定索引处的值:

var literalArray = ["逗哥","刀哥"] //!< ["逗哥", "刀哥"]
literalArray[0] = "加油"
print(literalArray) //!< ["加油", "刀哥"]

使用范围下标一次性更改值,即使替换值的数组长度与要替换的范围不同。但是必须不能越界:下标范围必须在当前数组的有效范围内。

var totalArray = ["飞哥", "强哥", "finally", "ok", "拯救", "静", "e"]
totalArray[...3] = ["","","","❌"]//!< ["", "", "", "❌", "拯救", "静", "e"]
totalArray[5..<7] = ["融合了"]//!< 5..<7有两个元素 被替换成了一个 log:["", "", "", "❌", "拯救", "融合了"]
print(totalArray)

使用insert(_:at :)方法在指定索引处将指定项插入数组:

var literalArray = ["逗哥","刀哥"] //!< ["逗哥", "刀哥"]
literalArray.insert("妄言", at: 1)//!< ["逗哥", "妄言", "刀哥"]
print(literalArray)

使用remove(at :)方法从数组中删除指定项并返回删除项

var literalArray = ["逗哥","刀哥"] //!< ["逗哥", "刀哥"]
literalArray.insert("妄言", at: 1)//!< ["逗哥", "妄言", "刀哥"]
let deleteStr = literalArray.remove(at: 0)//!< ["妄言", "刀哥"]
print(literalArray,"删除了:\(deleteStr)")//!< ["妄言", "刀哥"] 删除了:逗哥

使用removeLast()从数组中删除最终项并返回删除项。以避免需要查询数组的count属性.

var literalArray = ["逗哥","刀哥"] //!< ["逗哥", "刀哥"]
literalArray.removeLast()
//!< 也可以removeFirst()
literalArray.removeFirst()
  1. 数组的遍历
    使用for-in循环遍历
var literalArray = ["逗哥","刀哥"] //!< ["逗哥", "刀哥"]
for item in literalArray {
    print(item,"", separator: " ", terminator: "")//!< 逗哥 刀哥
}

使用enumerated()方法迭代数组:同时遍历各项的整数索引及其值。
返回由整数和各项的值组成的元组。

for item in literalArray.enumerated() {
    print(item.1) //!< 逗哥 刀哥
    print(item.0) //!< 0 1
    print(item) //!< (offset: 0, element: "逗哥") (offset: 1, element: "刀哥")
}

集合:
Set是唯一值的无序集合,这个特性可以让我们在适当的场景下去使用。

  1. 集合类型的值(散列,哈希)
    必须是可散列的类型才能存储在集合中。 即:集合中存储的类型必须提供能够计算自身散列值的方法。哈希值是一个Int值,对于所有相互比较相等的对象都是相同的,例如a == b,则遵循a.hashValue == b.hashValue
    默认情况下,Swift的所有基本类型,如StringIntDoubleBool都是可哈希的,并且可以用作集合中的值类型或字典的key类型。默认情况下,没有关联值的枚举类型也是可以哈希的。
    注意点:我们可以使用自定义符合Hashable协议的类型作为集合中的值类型或字典的key类型。符合Hashable协议的类型必须提供名为hashValueInt类型的可访问的属性。该自定义类型的hashValue属性在同一程序的不同执行或不同程序中执行时返回的值不需要相同。因为Hashable协议继承Equatable协议,所以符合Hashable协议的类型还必须提供运算符==的实现。
    Equatable协议要求任何符合==实现的,都是相等关系。==的实现必须要满足三个条件:反身性,对称性,及物性。
    比如实现Equatable协议的类型的值a,b,c:
    反身性表示:a == a
    对称性表示:a == b则b == a
    及物性表示:a == b && b == c则 a == c
  2. 集合类型语法:Set <Element>,其中Element是允许集合存储的类型。与数组不同,集合没有等效的简写形式。
  3. 创建空集合:
let emptySet = Set<Int>()
let emptyCharacterSet = Set<Character>()

如果上下文已经提供了集合中的类型信息,则集合中存储的值得类型就是确定的。

  1. 使用数组创建集合
    使用数组初始化集合,将一个或多个值写入集合中。
var favoriteGenres :Set<String> = ["可是","怎么","能够","如此"]
//! 由于Swift的类型推断的存在,在上下文提供了类型信息后,可以简写为
var favoriteGenres :Set = ["可是","怎么","能够","如此"]
  1. 集合的访问和修改
    count属性:访问集合中元素的个数。isEmptyBool类型的属性值用于集合判空。
if favoriteGenres.isEmpty {
    print("favoriteGenres集合中没有元素")
    
} else {
    print("favoriteGenres集合有 \(favoriteGenres.count)个元素")
    
}

使用insert(_:)方法添加一个新项到集合中:

favoriteGenres.insert("聪明")//!< ["聪明", "能够", "可是", "怎么", "如此"]

使用remove(_ :)方法从集合中删除指定项并返回删除项:如果该项目是该集合的成员,则删除该项目,并返回已删除的值,如果该集合不包含该项目,则返回nil

if let removeItem = favoriteGenres.remove("聪明") {
    print("\(removeItem)此项已被移除")
} else {
    print("favoriteGenres集合中没有该项")
}

使用contains(_:)方法,判断集合中是否包含某一项。

if favoriteGenres.contains("好的") {
    print("包含")
} else {
    print("不包含")
}
  1. 集合的遍历
    使用for-in循环遍历
var favoriteGenres :Set = ["可是","怎么","能够","如此"]
for item in favoriteGenres {
    print(item,"",separator: " ", terminator: "")//!< 能够 怎么 如此 可是
}

使用sorted()方法,以特定顺序遍历集合的值,该方法将集合的元素使用<运算符排序后的元素作为数组返回。

for item in favoriteGenres.sorted() {
    print(item)
}
  1. 集合的操作
    两组集合a和b以阴影区域表示的各种集合操作的结果。


    集合操作图示
  • 使用intersection(_ :)方法创建一个只包含两个集合共有的值的新集合。
  • 使用symmetricDifference(_ :)方法创建一个新集合,其中包含任一集合中的值,但不包含两个集合共同的部分。
  • 使用union(_ :)方法创建一个包含两个集合中所有值的新集合。
  • 使用subtracting(_:)方法创建一个值不在指定集中的新集。
let set1 : Set<Int> = Set.init(arrayLiteral: 1,5,7,2)
let set2 : Set<Int> = [2,4,6,8]
let set3 = set1.intersection(set2)//!< 预期:[2] 实际 [2]
//除了相同以外的其他元素
let set4 = set1.symmetricDifference(set2)//!< 预期:[1,5,7,4,6,8] 实际: [6, 4, 5, 7, 8, 1]
let set5 = set1.union(set2)//!< 预期[6, 4, 5, 7, 8, 1,2] 实际 [4, 5, 2, 8, 1, 7, 6]
//从set1中去除与set2有关的所有元素
let set6 = set1.subtracting(set2)//!< 预期[1,5,7] 实际[1, 7, 5]
  1. 集合之间的关系与相等判断:三个集合a,b和c,集合a包含集合b中的所有元素,则称集合a是集合b的超集,相反,集合b是集合a的子集。集合b和集合c没有共同的元素。集合a和集合c有共同的元素,则集合a和集合c相交。
    •使用运算符==确定两个集合是否包含所有相同的值。
    •使用isSubset(of :)方法确定集合的所有值是否包含在指定集合中。
    •使用isSuperset(of :)方法确定集合是否包含指定集合中的所有值。
    •使用isStrictSubset(of :)或isStrictSuperset(of :)方法来确定集合是否是指定集合的​​子集或超集,但不能判断相等。
    •使用isDisjoint(with :)方法确定两个集合是否没有有共同的值。
let set : Set<Int> = [1,5,2,7]
let set1 : Set<Int> = Set.init(arrayLiteral: 1,5,7,2,8)
//! 判断相等
if set == set1 {
    print("相等")
} else {
    print("不相等")
}
//!判断子集与超集
if set.isStrictSubset(of: set1){//!< set是否是set1的子集
    print("set是set1的子集")
}
if set1.isStrictSubset(of: set){//!< set1是否是set的超集
    print("set1是set的超集")
}
//!< 判断是否不相交
if set1.isDisjoint(with: set) {//!< `true`元素不相交 否则`false`
    print("set1和set不相交")
} else {
    print("set1和set相交")
}

字典:

  1. 字典类型的简写语法:
    a.Dictionary <Key,Value>。b. [Key:Value]。其中Key表示字典中键的值的类型,Value表示存储的值的类型。
    字典Key类型必须符合Hashable协议,就像集合的值类型一样。
  2. 创建一个空的字典:
//方式一
let namesOfIntegers :Dictionary<Int,String> = Dictionary<Int,String>.init()
let namesOfIntegers  = Dictionary<Int,String>.init()
let namesOfIntegers  = Dictionary<Int,String>()

//方式二
let namesOfIntegers  = [Int:String].init() //!< [:]
let namesOfIntegers = [Int:String]()
//方式三:提供类型信息
let namesOfIntegers : [Int:String] = [:]

如果上下文已经提供了字典中的类型信息,则字典中的类型信息就是确定的。

  1. 创建带初始值的字典:
let airports : [String:String] = [String:String].init(dictionaryLiteral: ("name","zhangfei"),("age","16"),("职业","将军"))//!< ["职业": "将军", "age": "16", "name": "zhangfei"]

简写语法:[key 1: value 1, key 2: value 2, key 3: value 3]

//键值类型都相同,swift乐行推断,故省略字典类型
let airports = ["职业": "将军", "age": "16", "name": "zhangfei"]
//以下类型的字典就不能类型推断,必须声明字典的键值类型
 let airports : [String : Any] = ["职业": "将军", "age": 16, "name": "zhangfei"]
  1. 字典的访问和修改
    count属性:访问字典键值对的个数。isEmpty属性值Bool类型,字典判空
if airports.isEmpty {
    print("airports是空的")
} else {
    print("airports:\(airports)中有\(airports.count)个元素")//!< airports:["name": "zhangfei", "职业": "将军", "age": "16"]中有3个元素
}

添加新项到字典中

//方式一.使用下标语法
var airports = ["job": "将军", "age": "16", "name": "zhangfei"]
airports["sex"] = "男"
//方式二
var airports = ["job": "将军", "age": "16", "name": "zhangfei"]
airports.updateValue("男", forKey: "sex")
print(airports)//!< ["sex": "男", "job": "将军", "age": "16", "name": "zhangfei"]

更改与特定键关联的值

//方式一.使用下标语法
var airports = ["job": "将军", "age": "16", "name": "zhangfei"]
airports["job"] = "主公"
//方式二
var airports = ["job": "将军", "age": "16", "name": "zhangfei"]
airports.updateValue("主公", forKey: "job")
print(airports)//!< ["age": "16", "job": "主公", "name": "zhangfei"]

上述示例中updateValue(_:forKey:)方法,若是key在字典中已存在,则更改其对应的值为新值,并且返回其对应的旧值。若key在字典中不存在,则返回nil,并且添加这个新的键值对到字典中,因为返回值可nil故此方法的返回值是可选类型。

if let oldValue =  airports.updateValue("男", forKey: "sex") {
    print("key早就已经存在了,并且旧值为\(oldValue)")
} else {
    print("key不存在,需添加到字典中")//!< log:key不存在,需添加到字典中
}

删除字典中键值对:

//方式一.使用下标语法
var airports = ["job": "将军", "age": "16", "name": "zhangfei"]
airports["job"] = nil
//方式二
if let value = airports.removeValue(forKey: "job") {
     print("The value \(value) was removed.")//!< The value 将军 was removed.
}
print(airports)//!< ["age": "16", "name": "zhangfei"]

上述示例中removeValue(_:forKey:)方法,若是key在字典中已存在,则移除,并且返回该移除项的值。若key在字典中不存在,则返回nil,(不存在键值对,无法做移除)因为返回值可nil故此方法的返回值是可选类型。

  1. 字典的遍历:
    使用for-in循环遍历字典中的键值对。字典中的每项都会作为(key, value)这样的元组类型返回,并且我们可以再迭代的过程中将元组的成员分解为临时常量或变量。
//! 方式一
for item in airports {
print("\(item.key)")
print("\(item.value)")
}
//! 方式二
for (key,value) in airports {
print("\(key)")
print("\(value)")
}

单独遍历其键或值

for key in airports.keys {
    print(key)
}
for value in airports.values {
    print(value)
}

keysvalues的类型转换为数组。不可强转,类型不一致,需要重新初始化为Array类型

let keysArray = [String](airports.keys)
let valuesArray = Array<String>.init(airports.values)

Swift的Dictionary类型是无序的。要按特定顺序遍历字典的键或值,需要在字典的 keysvalues 属性的基础上使用sorted()方法(升序),得到有序的keys或values。

参考资料:
swift 5.1官方编程指南


推荐文章:
iOS 解析一个自定义协议
iOS13 DarkMode适配(二)
iOS13 DarkMode适配(一)
2019苹果秋季新品发布会速览
申请苹果开发者账号的流程
Sign In With Apple(一)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,688评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,559评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,749评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,581评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,741评论 3 271
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,684评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,122评论 2 292
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,847评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,441评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,939评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,333评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,783评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,275评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,830评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,444评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,553评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,618评论 2 249

推荐阅读更多精彩内容

  • 集合类型 Swift提供三种主要的集合类型,称为Array,Set和Dictionary,用于存储值的集合。Arr...
    沐灵洛阅读 341评论 0 3
  • 案例代码下载 集合类型 Swift提供三种主要的集合类型,为数组,集合和字典,用于存储集合值。数组是有序的值集合。...
    酒茶白开水阅读 434评论 0 0
  • Swift 语言提供Arrays、Sets和Dictionaries三种基本的集合类型用来存储集合数据。数组(Ar...
    穷人家的孩纸阅读 528评论 3 2
  • Swift提供了三种主要的集合类型,array数组, set集合, dictionary字典,用于存储值集合。数组...
    微笑中的你阅读 499评论 0 0
  • 9月27日,中国女排在世界杯上3-1战胜荷兰队,迎来九连胜,离冠军又进一步。 她们目标明确,郎平教练在采访时说出一...
    李祥鸿阅读 64评论 0 1