返回

逐句解读Swift标准库源码 - Array和ContiguousArray

IOS

概述

数组(Array)和连续数组(ContiguousArray)是Swift标准库中非常重要的两个数据结构。它们都用于存储和管理有序元素,但它们在实现方式和使用场景上存在一些差异。在这篇文章中,我们将逐句解读这两个数组类型的源码,深入了解它们的实现细节和使用方法。

ContiguousArray

ContiguousArray与Array的不同之处在于ContiguousArray内的所有元素都是连续存储的,也就是存储在同一块连续的内存空间中。这使得ContiguousArray在某些场景下具有更好的性能,例如在需要快速访问数组中的元素时。但与此同时,ContiguousArray也存在一些限制,例如它不能包含nil值,并且不能动态调整大小。

ContiguousArray的实现

public struct ContiguousArray<Element> : RandomAccessCollection, MutableCollection, RangeReplaceableCollection {

    /// The elements of the collection.
    public var elements: UnsafeMutableBufferPointer<Element>

    /// The number of elements the collection contains.
    public var count: Int {
        return elements.count
    }

    /// A Boolean value indicating whether the collection is empty.
    public var isEmpty: Bool {
        return count == 0
    }

    /// Access the element at `position`.
    ///
    /// - Complexity: O(1)
    public subscript(position: Int) -> Element {
        get {
            return elements[position]
        }
        set {
            elements[position] = newValue
        }
    }

    /// A type representing the indices that are valid for subscripting the collection.
    public typealias Index = Int

    /// The index of the first element of the collection.
    public var startIndex: Int {
        return 0
    }

    /// The index of the element immediately after the last element of the collection.
    public var endIndex: Int {
        return count
    }

    /// Returns the position immediately after `i`.
    ///
    /// - Complexity: O(1)
    public func index(after i: Int) -> Int {
        return i + 1
    }

    /// Forms an array from the sequence of elements in the given collection.
    ///
    /// - Complexity: O(*n*)
    public init<S : Sequence>(_ elements: S) where S.Element == Element {
        self.elements = UnsafeMutableBufferPointer(start: UnsafeMutablePointer<Element>.allocate(capacity: elements.underestimatedCount),
                                                 count: elements.underestimatedCount)
        var i = startIndex
        for element in elements {
            self[i] = element
            formIndex(after: &i)
        }
    }

    /// Creates an empty array.
    ///
    /// - Complexity: O(1)
    public init() {
        elements = UnsafeMutableBufferPointer(start: nil, count: 0)
    }

    /// Creates an array containing a single element.
    ///
    /// - Complexity: O(1)
    public init(_ element: Element) {
        self.init(repeating: element, count: 1)
    }

    /// Creates an array containing the elements of a given sequence, repeated the specified number of times.
    ///
    /// - Complexity: O(*n*)
    public init<S : Sequence>(repeating repeatedValue: S.Element, count: Int) where S.Element == Element {
        self.elements = UnsafeMutableBufferPointer(start: UnsafeMutablePointer<Element>.allocate(capacity: count),
                                                 count: count)
        for i in startIndex..<endIndex {
            self[i] = repeatedValue
        }
    }

Array

Array与ContiguousArray的最大区别是Array中的元素可以是不连续存储的。这意味着Array可以包含nil值,并且可以动态调整大小。

Array的实现

public struct Array<Element> : RandomAccessCollection, MutableCollection, RangeReplaceableCollection {

    /// The elements of the collection.
    public var _buffer: ContiguousArray<Element>

    /// The number of elements the collection contains.
    public var count: Int {
        return _buffer.count
    }

    /// A Boolean value indicating whether the collection is empty.
    public var isEmpty: Bool {
        return _buffer.isEmpty
    }

    /// Access the element at `position`.
    ///
    /// - Complexity: O(1)
    public subscript(position: Int) -> Element {
        get {
            return _buffer[position]
        }
        set {
            _buffer[position] = newValue
        }
    }

    /// A type representing the indices that are valid for subscripting the collection.
    public typealias Index = Int

    /// The index of the first element of the collection.
    public var startIndex: Int {
        return _buffer.startIndex
    }

    /// The index of the element immediately after the last element of the collection.
    public var endIndex: Int {
        return _buffer.endIndex
    }

    /// Returns the position immediately after `i`.
    ///
    /// - Complexity: O(1)
    public func index(after i: Int) -> Int {
        return _buffer.index(after: i)
    }

    /// Forms an array from the sequence of elements in the given collection.
    ///
    /// - Complexity: O(*n*)
    public init<S : Sequence>(_ elements: S) where S.Element == Element {
        _buffer = ContiguousArray(elements)
    }

    /// Creates an empty array.
    ///
    /// - Complexity: O(1)
    public init() {
        _buffer = ContiguousArray()
    }

    /// Creates an array containing a single element.
    ///
    /// - Complexity: O(1)
    public init(_ element: Element) {
        _buffer = ContiguousArray(element)
    }

    /// Creates an array containing the elements of a given sequence, repeated the specified number of times.
    ///
    /// - Complexity: O(*n*)
    public init<S : Sequence>(repeating repeatedValue: S.Element, count: Int) where S.Element == Element {
        _buffer = ContiguousArray(repeating: repeatedValue, count: count)
    }

性能比较

在某些场景下,ContiguousArray的性能会优于Array。例如,在需要快速访问数组中的元素时,ContiguousArray的连续存储方式可以减少内存寻址的时间,从而提高访问速度。另外,在需要对数组进行频繁的插入和删除操作时,ContiguousArray的连续存储方式也可以减少内存碎片,从而提高性能。

然而,在某些场景下,Array的性能可能会优于ContiguousArray。例如,在需要存储大量元素的数组时,Array的动态调整大小的能力可以减少内存浪费,从而提高性能。另外,在需要对数组进行频繁的排序和搜索操作时,Array的内置排序和搜索算法可能会优于ContiguousArray的通用算法。

总结

Array和ContiguousArray是Swift标准库中非常重要的两个数据结构。它们都用于存储和管理有序元素,但它们在实现方式和使用场景上存在一些差异。在选择使用Array还是ContiguousArray时,需要考虑数组的具体使用场景和性能要求。