对于 Sequence 协议来说,表达的是既可以是一个有限的集合,也可以是一个无限的集合,而它只需要提供集合中的元素和如何访问这些元素的接口即可;对于 Collection 来说它是一个继承自 Sequence 的协议
1、Sequence
- Sequence 是通过
Iterator来访问元素的;Iterator 是一个迭代器let numbers = [2, 4, 6, 8] for num in numbers { print(num) }- 当执行 for in 的时候,本质上会通过 Collection 创建一个迭代器,然后把当前的数组传给迭代器,最后调用迭代器的
next方法将数组的元素遍历出来
- 我们来找到 Sequence.swift 这个文件,
IteartorProtocol是一个一次提供一个序列值的类型,它和 Sequence 协议是息息相关的, Sequence 每次通过创建迭代器来访问序列中的元素 - 所以我们每次在使用 for..in 的时候,其实都是 使用这个集合的迭代器来遍历当前集合或者序列当中的元素
- 当执行 for in 的时候,本质上会通过 Collection 创建一个迭代器,然后把当前的数组传给迭代器,最后调用迭代器的
IteratorProtocol与Sequence
- IteratorProtocol 中的内容:
public protocol IteratorProtocol { /// The type of element traversed by the iterator. associatedtype Element mutating func next() -> Self.Element? } - Sequence 中的内容:
public protocol Sequence { /// A type representing the sequence's elements. associatedtype Element where Self.Element == Self.Iterator.Element /// A type that provides the sequence's iteration interface and /// encapsulates its iteration state. associatedtype Iterator : IteratorProtocol /// Returns an iterator over the elements of this sequence. func makeIterator() -> Self.Iterator // other function // ...... }
通过 Sequence 表达有限集合
- 自定义的迭代器如下:
struct LZSequence: Sequence { typealias Element = Int var arrCount: Int func makeIterator() -> LZIterator { return LZIterator(self) } } struct LZIterator: IteratorProtocol { typealias Element = Int let seq: LZSequence init(_ seq: LZSequence) { self.seq = seq } var count = 0 mutating func next() -> Int? { guard count < seq.arrCount else { return nil } count += 1 return count } } let seq = LZSequence(arrCount: 10) for element in seq { print(element) } //打印结果 1 2 3 4 5 6 7 8 9 10
2、Collection
- Collection协议是建立在Sequence协议之上的,为有限的序列提供下标访问的能力,同时增加了count属性,自定义索引等特性
- 是一个序列,其元素可以被多次遍历;通过定义 startIndex 和 endIndex 属性,表示集合起始和结束位置
2.1、环形数组
-
普通数组来说,头尾的下标都是固定的,头下标为 0,尾下标为 length - 1。而环形数组(circular array)通常使用两个额外的数字去标识数组的头下标和尾下标
struct RingBuffer<Element>{ internal var _buffer: ContiguousArray<Element?> internal var headIndex: Int = 0 internal var tailIndex: Int = 0 internal var mask: Int{ return self._buffer.count - 1 } init(initalCapacity: Int) { let capcatiy = initalCapacity.nextPowerOf2() self._buffer = ContiguousArray<Element?>.init(repeating: nil, count:capcatiy) } mutating func advancedTailIndex(by: Int){ self.tailIndex = self.indexAdvanced(index: self.tailIndex, by: by) } mutating func advancedHeadIndex(by: Int){ self.headIndex = self.indexAdvanced(index: self.headIndex, by: by) } func indexAdvanced(index: Int, by: Int) -> Int{ return (index + by) & self.mask } mutating func append(_ value: Element){ _buffer[self.tailIndex] = value self.advancedTailIndex(by: 1) if self.tailIndex == self.headIndex { fatalError("out of bounds") } } mutating func read() -> Element?{ let element = _buffer[self.headIndex] self.advancedHeadIndex(by: 1) return element } }这个结构体通过一个 ContiguousArray 类型的 _buffer 来记录环形数组的元素,ContiguousArray 可以理解为存粹的 Swift 的数组。和 OC 没有任何关系的数组,它的效率比 Array 更快。
接下来我们通过 headIndex 和 tailIndex 来表示环形数组的起始和结束的位置。剩下的都是这个环形数组的一些初始化方法,移动环形数组的起始和结束的位置的方法,以及追加元素和读取元素的方法和获取元素索引的方法。
我们看到初始化方法,nextPowerOf2 这个方法表示 2^n,这个表示使 capacity 始终为 2^n,其实现如下:
extension FixedWidthInteger { /// Returns the next power of two. @inlinable func nextPowerOf2() -> Self { guard self != 0 else { return 1 } return 1 << (Self.bitWidth - (self - 1).leadingZeroBitCount) } }
2.2、MutableCollection
-
MutableCollection 允许集合通过下标修改自身元素。根据这一点我们对 RingBuffer 进行一个 extension,代码如下:
extension RingBuffer: Collection, MutableCollection { var startIndex: Int{ return self.headIndex } var endIndex: Int{ return self.tailIndex } subscript(position: Int) -> Element? { get{ return self._buffer[position] } set{ self._buffer[position] = newValue } } func index(after i: Int) -> Int { return (i + 1) & self.mask } }在这里 RingBuffer 遵循 Collection 协议,此时我们就需要实现 startIndex、endIndex 和 index(after:) 方法,index(after:) 是为了便于移动当前索引的位置。
遵守 MutableCollection 协议是因为为了实现下标的 setter 方法,便于在语法上直接通过下标来访问并修改这个元素的值,例如数组:
var nums = [1, 2, 3, 4] // 直接通过下标来修改元素的值 nums[2] = 8
2.3、RangeReplaceableCollection
- RangeReplaceableCollection 允许集合修改任意区间的元素
RangeReplaceableCollection 协议要求我们实现一个 init 方法,所以在这里就实现了一个默认的 init。接下来实现一个 remove(at:) 方法,对于环形数组来说,当我们删除一个元素的时候,首先把这个元素剔除,然后后面的元素往前移一个位置,然后再把尾节点指向当前空闲节点的位置。如果我们删除的位置,刚好是 headIndex 的位置,后面的元素也要往前移一个位置。那 remove(at:) 的实现如下:extension RingBuffer: RangeReplaceableCollection { init() { self.init(initalCapacity: 10) } func remove(at position: Int) -> Element? { // ...... return nil } }mutating func remove(at position: Int) -> Element? { let element = _buffer[position] _buffer[position] = nil switch position { case headIndex: advancedHeadIndex(by: 1) default: var currentIndex = position var nextIndex = indexAdvanced(index: position, by: 1) while nextIndex != tailIndex { _buffer.swapAt(currentIndex, nextIndex) currentIndex = nextIndex nextIndex = indexAdvanced(index: currentIndex, by: 1) } advancedTailIndex(by: -1) } return element }
2.4、BidirectionalCollection
-
BidirectionalCollection 可以向前或向后遍历集合。比如可以获取最后一个元素、反转序列、快速的获取倒序等
既然正序是通过 subscript 和 index(after:) 来实现的,那么倒序添加一个 index(before:) 就可以往前递归了,这就好像双向链表 一样,只不过双向链表获取的是值,而这里的集合获取的都是索引,代码如下:
extension RingBuffer: BidirectionalCollection{ func index(before i: Int) -> Int { return (i - 1) & self.mask } }
2.5、RandomAccessCollection
- RandomAccessCollection 可以任意访问集合元素
extension RingBuffer: RandomAccessCollection{ func index(_ i: Int, offsetBy distance: Int) -> Int { return (i + distance) & self.mask } func distance(from start: Int, to end: Int) -> Int { return end - start } } //测试代码 var buffer: RingBuffer = RingBuffer<Int>.init(initalCapacity: 10) for i in 0..<10 { buffer.append(i) } print(buffer.index(0, offsetBy: 2)) // 2