Swift集合类

97 阅读11分钟

集合类

数组

字面量创建
  • 可以使用数组字面量来初始化一个数组,它是一种以数组集合来写一个或者多个值的简写方式。数组字面量写做一系列的值,用逗号分隔,用方括号括起来。
let array = [1,2,3,4]
字面量创建空数组
  • 创建空数组的时候必须携带类型信息
  • 如果内容已经提供了类型信息,比如说作为函数的实际参数或者已经分类了的变量或常量,可以通过空数组字面量来创建一个空数组
let array = []

image.png

初始化器
  • 使用初始化器有两种方式
  • [类型]()
  • Array<类型>()
var myArray = [String]()
var myArray2 = Array<String>()
初始化器参数
  • init(repeating repeatedValue:Element,count:Int)
  • init(arrayLiteral elements:Element...)
let fiveZs = Array(repeating:"Z",count:5)
print(fiveZs)
// 打印结果为: ["Z","Z","Z","Z","Z"]
  • init<"S">(_ elements:S) where S:Sequence,Self.Element == S.Element
  • init(from decoder:Decoder) throws

image.png

数组遍历
  • 同时得到索引和值 enumerated()

image.png

image.png

  • 使用Iterator遍历数组

image.png

索引
  • startIndex返回第一个元素的位置,对于数组来说,永远都是0.
  • endIndex返回最后一个元素索引+1的位置,对于数组来说,等同于count.
  • 如果数组为空,startindex等于endIndex
判断是否包含指定元素
  • contains(_:)判断数组是否包含指定元素
  • contains(where:)判断数组是否包含复合给定条件的元素
判断所有元素符合某个条件
  • allSatisfy(_:)判断数组的每一个元素都符合给定的条件

image.png

查找元素
  • first返回数组第一个元素(optional),如果数组为空,返回nil
  • last返回数组最后一个元素(optional),如果数组为空,返回nil
  • first(where:)返回数组第一个符合给定条件的元素(optional)
  • last(where:)返回数组最后一个符合给定条件的元素(optional)

image.png

查找索引
  • firstIndex(of:)返回给定的元素在数组中出现的第一个位置(optional)
  • lastIndex(of:)返回给定的元素在数组中出现的最后一个位置(optional)

image.png

image.png

查找最大最小元素
  • min()返回数组中最小的元素
  • max()返回数组中最大的元素

image.png

image.png

数组添加和删除
在末尾添加
  • append(_:)在末尾添加一个元素
  • append(contentsOf:)在末尾添加多个元素

image.png

在任意位置插入
  • insert(_:at:)在指定的位置插入一个元素
  • insert(contentsOf:at:)在指定位置插入多个元素

image.png

字符串也是Collection
  • 字符串也是Collection,Element是Character类型

image.png

移除单个元素
  • remove(at:)移除并返回指定位置的一个元素
  • removeFirst()移除并返回数组的第一个元素
  • popFirst()移除并返回数组的第一个元素(optional),数组为空返回nil

image.png

image.png

移除多个元素
  • removeFirst(:)移除数组前面多个元素
  • removeLast(:)移除数组后面多个元素

image.png

  • removeSubrange(_:)移除数组中给定范围的元素
  • removeAll()移除数字所有元素
  • remove(keepingCapacity:)移除数字所有元素,保留数组容量

image.png

image.png

数组切片 移除多个元素
  • ArraySlice是数组或者其他ArraySlice的一段连续切片,和原数组共享内存
  • 当要改变ArraySlice的时候,ArraySlice会copy出来,形成单独内存。
  • ArraySlice拥有和Array基本完全类似的方法

image.png

通过Drop得到ArraySlice
  • dropFirst(:)"移除"原数组前面指定个数的元素得到一个ArraySlice
  • dropLast(:)"移除"原数组后面指定个数的元素得到一个ArraySlice
  • drop(:)"移除"原数组符合指定条件的元素得到一个ArraySlice

image.png

通过prefix得到ArraySlice
  • prefix()获取数组前面指定个数的元素组成的ArraySlice
  • prefix(upTo:)获取数组指定位置(不包含指定位置)前面的元素组成的ArraySlice
  • prefix(throught:)获取数组到指定位置(包含指定位置)前面的元素组成的ArraySlice
  • prefix(while:)获取数组前面符合条件的元素(到第一个不符合条件的元素截止)组成的ArraySlice

image.png

通过suffix得到ArraySlice
  • suffix()获取数组后面指定个数的元组组成的ArraySlice
  • suffix(from:)获取数组从指定位置到结尾(包含指定位置)的元素组成的ArraySlice

image.png

通过Range得到ArraySlice
  • 可以通过对数组下标指定Range获取ArraySlice,可以使用闭区间、半开半闭区间、单侧区间、甚至可以只使用...来获取整个数组组成的ArraySlice

image.png

ArraySlice转为Array
  • ArraySlice无法直接赋值给一个Array的常量或变量,需要使用Array(slice)

image.png

ArraySlice和原Array相互独立的
  • ArraySlice和原Array是相互独立的,它们添加删除元素不会影响对方

image.png

数组重排
数组元素的随机化
  • shuffle()在原数组上将数组元素打乱,只能作用在数组变量上。
  • shuffled()返回原数组的随机化数组,可以作用在数组变量和常量上

image.png

数组的逆序
  • reverse()在原数组上将数组逆序,只能作用在数组变量上。
  • reversed()返回原数组的逆序"集合表示",可以作用在数组变量和常量上,该方法不会分配新内存空间

image.png

数组的分组
  • partition(by belongsInSecondPartition:(Element) throws -> Bool)将数组以某个条件分组,数组前半部分都是不符合条件的元素,数组后半部分都是符合条件的元素

image.png

数组的排序
  • sort()在原数组上将元素排序,只能作用于数组变量
  • sorted()返回原数组的排序结果数组,可以作用在数组变量和常量上

image.png

交换数组两个元素
  • swapAt(::)交换指定位置的两个元素

image.png

字符串数组拼接
  • joined()拼接字符串数组里的所有元素为一个字符串
  • joined(separator:)以给定的分隔符拼接字符串数组里的所有元素为一个字符串

image.png

元素为sequence数组的拼接
  • joined()拼接数组里的所有元素为一个更大的sequence
  • joined(separator:)以给定的分隔符拼接数组里的所有元素为一个更大的Sequence

image.png

image.png

数组的底层探秘
数组的协议结构

image.png

Sequence(序列)
  • 一个序列(sequence)代表的是一系列具有相同类型的值,你可以对这些值进行迭代

image.png

IteratorProtocol
  • sequence通过创建一个迭代器来提供对元素的访问。迭代器每次产生一个序列的值,并且当遍历序列时对遍历状态进行管理。
  • 当序列被耗尽时,next()应该返回nil
protol IteratorProtocol {
   associatedtype Element
   mutating func next() -> Element?
}
定义自己的Sequence

image.png

Collection
  • 一个Collection是满足下面条件的Sequence
  • 稳定的Sequence,能够被多次遍历且保持一致
  • 除了线性遍历以外,集合中的元素也可以通过下标索引的方式被获取到
  • 和Sequence不同,Collection类型不能是无限的

image.png

Array的迭代器

image.png

Array的下标访问

image.png

Array的buffer

image.png

_ContiguousArrayBuffer

image.png

_ContiguousArrayBuffer的 getElement

image.png

UnSafeMutablePoint的下标操作

image.png

问题:endIndex vs count

image.png

索引

image.png

image.png

课后探索
  • 学习removeFirst方法的源码,得出removeFirst的复杂度
  • 学习sort方法的源码,了解Array的排序方法
用数组实现栈
Stack
  • 栈(Stack)是一种后入先出(Last in First Out)的数据结构,仅限定在栈顶进行插入或者删除操作。栈结构的实际应用主要有数制转换、括号匹配、表达式求值等等。

image.png

image.png

Queue
  • 队列在生活中非常常见。排队等位吃饭、在火车站买票、通过高速路口等,这些生活中的现象很好的描述了对列的特点:先进先出(FIFO,first in first out),排在最前面的先出来,后面来的只能排在后面。

image.png

image.png

练习
  • 尝试改造Stack和Queue的代码让实现SeQuence协议,支持For-In循环

Set(集合)的概念

  • Set是指具有某种特定性质的具体的或抽象的对象汇总而成的集体。其中,构成Set的这些对象则称为该Set的元素。
集合的三个特性
  • 确定性:给定一个集合,任给一个元素,该元素或者属于或者不属于该集合,二者必居其一
  • 互斥性:一个集合中,任何两个元素都认为是不相同的,即每个元素只能出现一次
  • 无序性:一个集合中,每个元素的地位都是相同的,元素之间是无序的
Swift里面的集合
  • Swift的集合类型写做Set<Element">",这里的Element是Set要储存的类型。不同与数组,集合没有等价的简写。
创建Set
  • 使用初始化器语法来创建一个确定类型的空Set
  • 使用数组字面量创建Set

image.png

Set类型的哈希值
  • 为了能让类型储存在Set当中,他必须是可哈希的--就是说类型必须提供计算它自身哈希值的方法。
  • 所有Swift的基础类型(比如String,Int,Double和Bool)默认都是可哈希的,并且可以用于Set或者Dictionary的键。

image.png

  • 自定义类型需要实现Hashable协议

image.png

访问和修改Set(遍历Set)
  • 可以使用For-In遍历Set
  • 因为Set是无序的,如果顺序遍历Set,使用sorted()方法

image.png

访问Set
  • 使用count获取Set里元素个数
  • 使用isEmpty判断Set是否为空

image.png

添加元素
  • insert(_:)添加一个元素到Set
  • update(with:)如果已经有相等的元素,替换为新元素。如果Set中没有,则插入。

image.png

移除元素
  • filter(_:)返回一个新的Set,新Set的元素是原始Set符合条件的元素

image.png

  • remove(_:)从Set当中移除一个元素,如果元素是Set的成员就移除它,并且返回移除的值,如果集合没有这个成员就返回nil。
  • removeAll()移除所有元素

image.png

基本Set操作

image.png

基本Set操作的定义
  • intersection(_:)交集,由属于A且属于B的相同元素组成的集合,记作AnB(或BnA)
  • union(_:)并集,由所有属于集合A或属于集合B的元素所组成的集合,记作AuB(或BuA)
  • symmetricDifference对称(_:)对称差集,集合A与集合B的对称差集定义为集合A与集合B中所有不属于AnB的元素的集合
  • subtracting(_:)相对补集,由属于A而不属于B的元素组成的集合,称为B关于A的相对补集,记作A-B或A\B.

image.png

Set判断方法
  • isSubset(of:)判断是否是另一个Set获取Sequence的子集
  • isSuperset(of:)判断是否是另一个Set或者Sequence的超集
  • isStrictSubset(of:)和isStrictSuperset(of:)判断是否是另一个Set的子集或者超集,但是又不等于另一个Set
  • isDisjoin(with:)判断两个Set是否有公共元素,如果没有返回true,如果有返回false

image.png

Set(集合)的算法
  • 给定一个集合,返回这个集合所有的子集
思路1-位
  • 思路:解这道题的思想本质上就是元素选与不选的问题,于是我们就可以想到用二进制来代表选与不选的情况。"1"代表这个元素已经选择,而"0"代表这个元素没有选择。假如三个元素A B C,那么101就代表B没有选择,所以101代表的子集为AC。

image.png

思路2-递归
  • 思路:如果只有一个元素,那么它的子集有两个,分别是本身和空集,然后在已经有一个元素的子集的基础上,第二个元素有两种选法,那就是加入到前面的子集里面或者不加入到前面的子集里面,也就是选与不选的问题。而前面的子集一共有两个,对每一个子集都有来自于下一个元素的加入和不加入两种选法。那么就可以得出两个元素的子集一共有四个。依此类推,就可以得出n的元素所有子集(这里n个元素的子集一共有2n个,非空子集一共有2n-1个)。

image.png

Set的底层探究
从Set的insert说起

image.png

_NativeSet的find方法

image.png

HashTable

image.png

image.png

线性探测的开放寻执法

image.png

_NativeSet的insertNew

image.png

HashTable的insertNew

image.png

_NativeSet的uncheckedInitialize

image.png

Dictionary
  • 字典储存无序的互相关联的同一类型的键和同一类型的值的集合
  • 字典类型的全写方式Dictionary<Key,Value>,简写方式[Key:Value],建议使用简写方式
  • 字典的key必须是可哈希的
创建空字典
  • 初始器方式
  • 简写方式
  • 字面量方式
var dict1 = Dictionary<String,Int>()
var dict2 = [String: Int]()
var dict3: Dictionary<String,Int> = [:] 
字面量创建字典
  • [key1:value1,key2:value2,key3:value3]
let dict = ["zhangsan":18,"lisi":19,"wangwu":20]
count和isEmpty
  • 可以使用count只读属性来找出Dictionary拥有多少元素
  • 使用布尔量isEmpty属性检查字典是否为空
遍历字典
  • For-in循环
  • 可以通过访问字典的keys和values属性来取回可遍历子带你的键或值的集合
  • Swift的Dictionary类型是无序的。要以特定的顺序遍历字典的键或值,使用键或值的sorted()

image.png

字典的常见操作
添加或更新元素
  • 使用下标脚本语法给一个键赋值nil来从字典当中移除一个键值对
  • 使用removeValue(forKey:)来从字典里移除键值对。这个方法移除键值对如果他们存在的话,并且返回移除的值,如果值不存在则返回nil
合并两个字典
  • merge(_:uniquingKeysWith:)

image.png

firstindex
  • 虽然字典是无序的,但是每个kv对在扩容之前的位置是稳定的。如果需要保持顺序的kv对可以使用KeyValuePairs

image.png

字典的底层探秘
从下标操作谈起

image.png

Dictionary._Variant的setValue

image.png

image.png

_NativeDictionary的_insert

image.png

_NativeDictionary的uncheckedInitialize

image.png

_NativeDictionary的findKey

image.png