我有数组,我想从头部删除N个元素。
可以说,数组(带浮点数)有1M个元素,我想先500K出去。我有两种方法,在循环中调用500K次调用或调用splice(0,500000)。
问题是,第一个解决方案是一个可怕的想法(它非常非常慢)。第二个也很慢,因为splice在一个新数组中返回从数组中删除的部分(好吧,它只是分配500K浮点数并将它们抛出窗口)。
在我的应用程序中,我正在使用非常大的矩阵做一些事情,不幸的是,通过拼接删除元素对我来说很慢。有没有更快的方法来实现它?
答案 0 :(得分:2)
我希望Array#slice
与其中任何一个选项一样快 ,并且可能更快。它确实意味着暂时分配重复的内存,但1M数量只有大约64MB的内存(假设JavaScript引擎已经能够使用真正的数组),所以暂时拥有原始的64MB加上32MB的内存你想要的在发布原始64MB之前保持相当便宜:
array = array.slice(500000);
这也有一个优点,就是它不会强制JavaScript引擎使用对象而不是封面下的数组。 (你正在做的其他事情可能会导致这种情况,但......)
你说你用浮点数做这个,你可能会看一下使用Float64Array
而不是无类型数组。这限制了您可以执行的操作,但确保您不会使用未经优化的数组。从数组中删除条目时,最终可能会得到未经优化的数组,其访问时间明显慢于优化数组,因为它们最终成为具有命名属性而非偏移访问的对象。 (如果可以的话,一个好的JavaScript引擎会对它们进行优化;使用类型化数组会有助于阻止你进行优化。)
这(断开并且肯定存在缺陷)NodeJS测试表明splice
比slice
慢了60%到95%,并且V8在保持阵列优化方面做得很好类型化数组的结果实际上与slice
情况下无类型数组的结果相同:
"use strict";
let sliceStats = createStats();
let sliceTypedStats = createStats();
let spliceStats = createStats();
for (let c = 0; c < 100; ++c) {
if (test(buildUntyped, sliceStats, testSlice).length != 500000) throw new Error("1");
if (test(buildTyped, sliceTypedStats, testSlice).length != 500000) throw new Error("2");
if (test(buildUntyped, spliceStats, testSplice).length != 500000) throw new Error("3");
console.log(c);
}
console.log("slice ", avg(sliceStats.sum, sliceStats.count));
console.log("sliceTyped", avg(sliceTypedStats.sum, sliceTypedStats.count));
console.log("splice ", avg(spliceStats.sum, spliceStats.count));
function avg(sum, count) {
return (sum / count).toFixed(3);
}
function createStats() {
return {
count: 0,
sum: 0
};
}
function buildUntyped() {
let a = [];
for (let n = 0; n < 1000000; ++n) {
a[n] = Math.random();
}
return a;
}
function buildTyped() {
let a = new Float64Array(1000000);
for (let n = 0; n < 1000000; ++n) {
a[n] = Math.random();
}
return a;
}
function test(build, stats, f) {
let a;
let ignore = 0;
let start = Date.now();
for (let i = 0; i < 10; ++i) {
let s = Date.now();
a = build();
ignore += Date.now() - s;
a = f(a);
}
stats.sum += Date.now() - start - ignore;
++stats.count;
return a;
}
function testSlice(a) {
return a.slice(500000);
}
function testSplice(a) {
a.splice(0, 500000);
return a;
}
答案 1 :(得分:1)
Immutable.js通过结构共享解决了这个问题。它不会像splice那样复制条目,但会在包含的内容中返回引用 阵列的一部分。您需要将数组移动到Immutable.js数据结构,然后调用不可变操作splice。