我想知道Swift如何在内部管理数组? Apple's language guide仅处理使用情况,但没有详细说明内部结构。
作为一名Java开发人员,我习惯将“裸”数组视为非常静态和固定的数据结构。我知道在Swift中这不是真的。在Swift中,除了在Java中,您可以改变数组的长度并执行插入和删除操作。在Java中,我习惯于根据我想用该结构执行的操作来决定我想要使用的数据结构(简单数组,ArrayList,LinkedList等),从而优化我的代码以获得更好的性能。
总之,我想知道如何在Swift中实现数组。它们内部管理为(双)链表吗?是否有可与Java的Collection Framework相媲美的东西,以便调整以获得更好的性能?
答案 0 :(得分:19)
您可以在Swift标准库的上面的注释中找到有关Array
的大量信息。要查看此内容,您可以在操场上cmd-opt-click Array
,也可以在非官方SwiftDoc页面查看。
从那里解释一些信息来回答你的问题:
在Swift中创建的数组将其值保存在连续的内存区域中。因此,您可以有效地将Swift数组传递到需要这种结构的C API。
正如您所提到的,数组可以在向其追加值时增长,并且在某些点处意味着分配了一个更大的新内存区域,并将先前的值复制到其中。正是由于这个原因,它声明像append 这样的操作可能<{1}} - 也就是说,执行追加操作的最坏情况时间与数组的当前大小成比例增长(因为复制值所花费的时间)。
但是,当阵列必须增加其存储空间时,每次分配的新存储量会呈指数级增长,这意味着重新分配会随着您的追加而变得越来越罕见,这意味着在所有呼叫上追加“分摊”的时间接近恒定时间。
数组也有一个方法O(n)
,它允许你通过请求数组预先为自己分配一些最小量的空间来预先避免重新分配调用append。如果您提前知道计划在阵列中保留多少个值,则可以使用此项。
在数组的中间插入一个新值也是reserveCapacity
,因为数组保存在连续的内存中,因此插入一个新值会将后续值混合到最后。不同于附加,这不会改善多个呼叫。这与您可以在O(n)
中插入的链接列表(即常量时间)非常不同。但请记住,最大的权衡是数组也可以在不变的链接列表中随机访问。
对阵列中单个值的原位更改(即通过下标分配)应为O(1)
(O(1)
实际上没有记录注释,但这是一个非常安全的选择)。这意味着如果您创建一个数组,填充它,然后不附加或插入它,它在性能方面应该与Java数组类似。
所有这一点都有一点需要注意 - 数组具有“价值”语义。这意味着如果你有一个数组变量subscript
,并将它分配给另一个数组变量a
,这实质上是复制数组。 b
中对值的后续更改不会影响a
,更改b
不会影响b
。这与“引用”语义不同,a
和a
指向同一个数组,通过b
对其进行的任何更改都会反映给通过{{1}查看它的人}。
但是,Swift数组实际上是“Copy-on-Write”。也就是说,当您将a
分配给b
时,实际上不会进行复制。只有当两个变量中的一个发生变化(“变异”)时才会发生。这带来了很大的性能优势,但它确实意味着如果两个数组引用相同的存储,因为自复制以来都没有执行写操作,像下标分配这样的更改确实会在此处重复整个数组的一次性成本点。
在大多数情况下,你不应该担心这种情况,除非在极少数情况下(特别是处理小到中等大小的数组时),但如果性能对你很重要,那么你自己肯定值得熟悉包含该链接中的所有文档。