所以我一直在尝试在JS中实现一个集合类型的类(类似于在C#中找到的List),它具有一些自定义功能。我也希望它有所优化(我已经阅读了一些关于如何正确使用JS Arrays的文章)。 所以我想我自己“如果我们没有为数组定义初始大小并且我们不断向它添加对象,那么在内部它将不得不为每个插入分配一个新的大小,这必须很慢。我可以通过分配来避免这种情况我自己的新尺寸(改变阵列长度),有点类似于在CSharp中完成的尺寸,每当达到最大容量时尺寸加倍(我知道这不是微不足道但它是一个开始)“
我试图实现这个想法并发现它慢得多(慢10倍):
//This simplified approach of my implementation is faster...
var array = [];
var counter = 0;
function addItem(newItem) {
array[++counter] = newItem;
}
//..than this version that resizes the array when a limit is reached
var array = [];
array.length = INITIAL_SIZE;
/*
Alternatively
var array = new Array(INITIAL_SIZE);
*/
var counter = 0;
function addItem(newItem) {
if( CheckCapacity(counter + 1) ) { //Function that checks if the maximum size is reached and if it is, change the array.length to the new size
array[++counter] = newItem;
}
}
在测试之前,我想,“因为当我调用CheckCapacity(计数器+ 1)时,我有一个新的数组大小,因此在内部它(JS数组)不需要进行相同的操作。第一个函数,因为我确保有空间可用,超过必要的“,即第二个函数上的数组[++ counter] = newItem 行应该比同一个函数中的相同第一个功能。 我甚至使用了不同的数组,其中包含预先计算出的大小的数据;它仍然较慢。
回到我的问题,JS Array的实现如何分配必要的大小?我是否正确地假设没有太多可以加快这个过程的速度?对我来说,有意义的是,每次添加新项目时,拥有一个动态分配更多内存的对象(JS数组)的缺点就是速度的损失(除非它实现了相当好的算法,但我不知道不知道,因此我的问题。
答案 0 :(得分:7)
在Javascript中,数组是一种抽象。它是如何实现的(以及何时执行分配和调整大小)由Javascript引擎决定 - ECMAScript规范没有规定如何完成。因此,基本上没有没有精确的方法来了解。
在实践中,Javascript引擎非常聪明地分配内存和确保不分配太多。在我看来,它们比C#的List
复杂得多 - 因为Javascript引擎可以根据情况动态地改变底层数据结构。算法各不相同,但大多数会考虑阵列中是否存在任何“漏洞”:
var array = [];
array[0] = "foo" // is a resizable array
array[1] = "bar" // is a resizable array
array[2] = "baz" // is a resizable array
array[1000000] = "hello"; // is now a hash table
console.log(array[1000000]) // "hello"
如果您正常使用数组并使用从零开始的连续键,则没有“漏洞”,并且大多数Javascript引擎将通过使用可调整大小的数组数据结构来表示Javascript数组。现在考虑第四个任务,我创造了一个大约一百万的所谓“洞”(洞跨越插槽3-999999)。事实证明,Javascript引擎足够聪明,不会为这个巨大的漏洞分配大约100万个内存插槽。它检测到我们有一个洞,它现在将使用Dictionary / hash-table(如数据结构)(它使用二进制搜索树,其中键被散列)来表示Javascript数组,以节省空间。它不会为洞存储空间,只有四个映射:(0, "foo")
,(1, "bar")
,(2, "baz")
,(1000000, "hello")
。
不幸的是,现在访问数组的引擎速度较慢,因为它现在必须计算哈希值并遍历树。当没有漏洞时,我们使用可调整大小的阵列,我们有更快的访问时间,但是当我们有一个漏洞时,Array的性能会更慢。常见的术语是指一个数组是密集数组,当它是没有任何漏洞时(它使用可调整大小的数组=更好的性能),而数组是< strong>稀疏数组,当带有一个或多个漏洞时(它使用散列表=性能较慢)。为了获得最佳性能,请尝试使用密集阵列。
现在结束,让我告诉你以下是一个坏主意:
var array = new Array(1000000);
array[0] = "foo"; // is a hash table
上面的数组有一个大小约为1百万的漏洞(就像这样:["foo", undefined, undefined, ... undefined]
)因此,它使用散列表作为底层数据结构。所以实现自己调整大小是一个坏主意 - 它会创建一个漏洞并导致最差的性能而不是更好。你只是混淆了Javascript引擎。
这是你的代码正在做的事情,你的数组总是有一个漏洞,因此使用哈希表作为底层数据结构;与没有任何漏洞的数组(也就是代码的第一个版本)相比,性能更慢。
我是否正确地假设没有太多可以加快这个过程的速度?
是,关于预分配空间,用户方面几乎没有做什么。一般来说,为了加速Javascript数组,你想避免创建稀疏数组(避免创建漏洞):
new Array(size)
预先分配。而是“随着你的成长”。引擎将计算出底层可调整大小的数组本身的大小。[JS阵列优于C#列表的缺点是它们]每次添加新项目时动态分配更多内存
不,不一定。当Javascript数组没有漏洞时,C#列表和Javascipt数组基本相同。两者都是可调整大小的数组区别在于: