我想知道var foo = new Array(20)
,var foo = [1,2,3]; foo.length = 10
或var foo = [,,,]
等代码的用例是什么(同样,为什么要使用delete
运算符代替只是从数组中删除项目)。您可能已经知道,所有这些都将导致稀疏数组。
但为什么我们允许做上述事情呢?为什么有人想创建一个默认情况下length
为20
的数组(如第一个示例中所示)?为什么有人想要修改和破坏数组的length
属性(如第二个例子中所示)?为什么有人想做[, , ,]
之类的事情?为什么要使用delete
而不是仅仅从数组中删除元素?
任何人都可以为这些陈述提供一些用例吗?
在其插槽中没有显式值但是具有暗示存在插槽的长度属性的数组是JS中一种奇怪的异域类型的数据结构,具有一些非常奇怪和混乱的行为。创建这样一个值的能力完全来自旧的,已弃用的历史功能(“类似于数组的对象”,如arguments对象)。
因此,本书暗示arguments
对象以某种方式在某处使用我上面列出的一个示例来创建稀疏数组。那么,arguments
在何处以及如何使用稀疏数组?
足够稀疏的数组通常以比密集数组更慢,更内存的方式实现。
“内存效率更高”看起来像是对我来说“慢”的矛盾,那么在稀疏阵列的上下文中,两者之间有什么区别呢? Here是指向该书特定部分的链接。
答案 0 :(得分:0)
我想知道代码的用例如var foo = new Array(20),var foo = [1,2,3]; foo.length = 10或var foo = [,,,]
理论上,出于同样的原因,人们通常使用稀疏数据结构(不一定按重要性顺序):内存使用情况(var x = []; x[0]=123;x[100000]=456;
不会消耗100000个'),性能(比如,取上述x的平均值,通过for-in或reduce())和方便(没有'难以超出界限的错误,不需要明确增长/缩小);
在语义上说,js数组只是一个特殊的关联集合,它带有索引键和一个特殊的属性' length'满足大于其所有索引属性的不变量。虽然它是一个非常优雅的定义,它的缺点是渲染稀疏定义的数组有点令人困惑,并且容易出错。
但为什么我们允许做上述事情?
即使我们不允许定义稀疏数组,我们仍然可以将未定义的元素放入数组中,导致与稀疏数组看到的基本相同的可用性问题。
因此,假设[0,undefined,...,undefined,1,undefined]
与[0,...,1,]
相同,只会给你带来更多内存消耗数组和更慢的迭代。
足够稀疏的数组通常以比密集数组更慢,更内存的方式实现。更有记忆效率和更慢看起来像是对我的矛盾
"密集阵列"用于通用数据的通常被实现为填充有相同大小的元素的连续存储块;如果添加更多元素,则继续填充内存块,如果用尽则分配新块。鉴于重新分配意味着将所有元素移动到新的存储块,所以通常大量分配所述存储器以最小化重新分配的机会(诸如黄金比率乘以最后的容量)。 因此,这种数据结构通常是有序/本地遍历最快(更多CPU /缓存友好),无法预测的插入/删除最慢(对于足够大的N)并且具有高内存开销~sizeof(elem)* N + extra未来元素的空间。
相反,"稀疏数组/矩阵/..."是通过“链接”来实现的。将较小的内存块一起扩展到内存中或使用一些“逻辑压缩”的内存块。密集数据结构的形式或两者;在任何一种情况下,由于显而易见的原因,内存消耗减少了,但是相对来说,遍历它们需要更多的工作和更少的本地内存访问模式。
因此,如果相对于相同的有效遍历元素进行比较稀疏数组消耗的内存要少得多,但比密集数组慢得多。但是,鉴于您使用稀疏数据和稀疏数据的算法以及简单地在零点上运行的算法,稀疏数组在某些情况下会变得更快(例如,将非常大的矩阵与很少的非零元素相乘... )。
答案 1 :(得分:0)
因为 JS 数组是一种非常奇怪的数据类型,它不遵守时间复杂度规则,正如您在使用正确的工具时可能期望的那样。我的意思是 for in
循环或 Object.keys()
方法。尽管我是一个非常实用的人,但我还是会转向这里的 for in
循环,因为它break
能够。
JS 中的稀疏数组有一些非常有益的用例,例如在 O(1) 中将项目插入和删除到已排序的数组中,如果您的值是像限价单这样的数字,则不会干扰已排序的结构。或者换句话说,如果您可以在键和值之间建立直接的数字关联。