Swift array.capacity vs array.count

Posted

技术标签:

【中文标题】Swift array.capacity vs array.count【英文标题】: 【发布时间】:2015-01-07 23:22:04 【问题描述】:

我了解 array.count (数组中的元素数)。 count 对于遍历数组元素很有用。我有点明白 array.capacity 的要点

capacity 一个整数值,表示总共有多少个元素 数组无需重新分配即可存储(只读)。

实验

我一直在玩 Playground,发现数组的容量是偶数(增加 2)

var arr = [1, 2, 3 , 4, 5, 6, 7]
arr.removeLast() // capacity stays the same after a removal
println(arr.capacity) // 8
println(arr.count)    // 6

var arr = [1, 2, 3 , 4, 5, 6]
arr.removeLast()
println(arr.capacity) // 6
println(arr.count)    // 5

问题

数组容量有什么用?请举个具体的例子?

【问题讨论】:

Afaik 这只是一种预分配内存空间的方式。不过,您将能够通过 Google 搜索找到它。不是一个真正的问题。另外,为什么你需要知道?计数不够好? 【参考方案1】:

数组的容量——特别是它的reserveCapacity 方法——让您可以在数组中预先分配空间。

如果您向数组添加元素并且超出了其容量,则该数组必须增加其容量。由于 Swift 数组将其元素连续存储在内存中,因此它必须重新分配其内部存储空间,并且(通常)将其所有元素从旧存储空间复制到新存储空间。 (请注意,NSArray 没有记录以连续存储其元素,但我们可以推断 Swift Array 可能基于 withUnsafeMutableBufferPointer 方法的存在。)

如果您提前知道要向数组中添加多少元素,则可以使用reserveCapacity 方法预设数组的容量,这样就不需要执行任何重新分配(以及相关的复制)。

我能想到的询问数组容量的唯一原因是了解系统的工作原理以及调试性能问题。

通常您无需担心预留容量。重新分配很少是性能问题。 Swift 使用(我相信)有效的重新分配计划,以便重新分配的数量在数组的最终计数中是对数的。例如。如果你一次添加一百万个元素,Swift 应该执行不超过 20-30 次重新分配。

但是,如果您知道您的数组会非常大(例如,Mac 上的千兆字节或 ios 设备上的几十兆字节),或者如果您在性能敏感的代码路径中填充数组(例如填充音频缓冲区将在几微秒内开始播放),您可能希望保留容量并避免重新分配。

您可能不应该担心保留容量,除非您知道重新分配是一个问题,因为分析器显示它们是一个瓶颈,或者因为您有其他证据(例如音频缓冲区示例中的音频故障)。

【讨论】:

为此添加一些上下文:map 方法使用它来预分配数组,这是map 在简单的for-in-append 循环(@ 987654328@ 几乎总是比等效的 for-in-append 快)。为了获得可测量的差异,您需要大约 10-1 亿个元素(取决于平台)。在此之下,差异太小而无法准确测量。但在超过 1000 万个元素的范围内,它确实变得不平凡,并且可能值得麻烦。对于非常大的阵列,我已经看到了大约 30% 的改进。 明确一点:我从来没有在“真正的”iOS 应用程序中遇到过这种情况,而且我做了很多高性能的 iOS 工作。我上面只有数字,因为我针对测试代码运行了微基准测试以找到边缘的位置。如果您遇到此问题,您应该已经询问是否应该使用 Accelerate,并且可能将您的工作转移到 C++(这更容易推断性能)。 我还最终将一些性能关键代码切换到 C++。【参考方案2】:

数组容量有什么用

基本上,数组容量没有外用。它供 Swift 内部使用。如果您知道要为该数组分配 100 个对象,您可以在创建数组时提前设置容量,我看到有些人在他们的代码中这样做;但是这样做并没有特别的需要,这样做也没有特别的收获。你已经在引擎盖下看到了一些你并不真正需要看到的东西。看过了就可以忘记了。

【讨论】:

“既然你已经看过了,你就可以忘记它了。” :) 你能举个例子它对外部使用有什么用处吗? @raychenon 为什么?对于 NSArray,Apple 开发人员特别不鼓励使用 arrayWithCapacity 并让一切自行工作。为什么你需要一个例子来说明如何使用你可能永远不应该使用的东西? @Fogmeister 因为尚未有人展示容量如何有用。睡前问个问题:) 那么问题应该是:为什么 Apple 开发人员不鼓励使用他们允许访问的东西?好吧,谷歌也好不到哪里去。 阵列容量的概念在对性能产生重大影响时非常重要。查找数组当前容量的能力可能只对调试和学习有用。【参考方案3】:

数组可以包含的元素总数

分配新的存储空间。

每个数组都会保留特定数量的内存来保存其内容。当您向数组添加元素并且该数组开始超出其保留容量时,该数组会分配更大的内存区域并将其元素复制到新存储中。新存储是旧存储大小的倍数。这种指数增长策略意味着追加一个元素在恒定时间内发生,平均许多追加操作的性能。触发重新分配的追加操作会降低性能,但随着数组变大,它们发生的频率会越来越低。

以下示例从数组字面量创建一个整数数组,然后附加另一个集合的元素。在追加之前,数组会分配足够大的新存储空间来存储结果元素。

var numbers = [10, 20, 30, 40, 50]

numbers.count == 5

numbers.capacity == 5

numbers.append(contentsOf: stride(from: 60, through: 100, by: 10))

numbers.count == 10

numbers.capacity == 12

【讨论】:

以上是关于Swift array.capacity vs array.count的主要内容,如果未能解决你的问题,请参考以下文章

Dart vs Swift

Swift VS Kotlin

Swift 4 添加手势:覆盖 vs @objc

在 Swift 中:Array VS NSArray VS [AnyObject] 之间的区别

text Swift Rounded vs round

swift debug print vs print