如何在Javascript中将大量元素附加到现有数组?

时间:2017-03-25 22:24:38

标签: javascript arrays

例如,我有两个非常大的数组ab,每个都有数百万个元素。我想将数组b的所有元素追加到数组a。此外,我不想创建新数组,但更改现有数组a(因此concat不是选项)。

我试过了:

Array.prototype.push.apply(a, b)

但对于非常大的数组,这给了我一个错误:

RangeError: Maximum call stack size exceeded

我知道我可以制作循环并使用push逐个添加元素。

有更好的方法吗?

2 个答案:

答案 0 :(得分:1)

根据这个问题(Is there a max number of arguments JavaScript functions can accept?)判断,一次可以安全追加的最大参数数量约为100,000。假设您的内存一次有100,000个项目的重复列表,您可以使用下面显示的切片方法。 (参见下面详述的拼接和涂抹方法)

但是,我将基准测试比较添加到不同形式的单个推送方法中,以确定在执行此操作时是否有任何性能优势,因为在引擎盖下,可能是拼接串联仍然是元素而不是批量memcpy操作。

const x = [];

for (let i = 0; i < 10000000; i += 1) {
    x.push(i);
}

const a = x.slice();
const b = x.slice();
const v = x.slice();
const y = x.slice();
const z = x.slice();


// append 100,000 at a time using splice
const sliceStart = new Date();
for (let i = 0; i < b.length; i += 100000) {
    const len = (i + 100000) % (b.length + 1);
    const c = b.slice(i, len);
    a.splice(a.length, 0, ...c);
}
const sliceEnd = new Date();

// append 100,000 using Array.prototype.push
const protoStart = new Date();
for (let i = 0; i < b.length; i += 100000) {
    const len = (i + 100000) % (b.length + 1);
    const c = b.slice(i, len);
    Array.prototype.push.apply(v, c);
}
const protoEnd = new Date();

// using for and push
const pushStart = new Date();
for (let i = 0; i < b.length; i += 1) {
    y.push(b[i]);
}
const pushEnd = new Date();

// using for and push
const batchPushStart = new Date();
for (let i = 0; i < b.length; i += 8) {
    y.push(b[i]);
    y.push(b[i + 1]);
    y.push(b[i + 2]);
    y.push(b[i + 3]);
    y.push(b[i + 4]);
    y.push(b[i + 5]);
    y.push(b[i + 6]);
    y.push(b[i + 7]);
}
const batchPushEnd = new Date();

// using forEach and push
const forEachStart = new Date();
b.forEach(i => z.push(i));
const forEachEnd = new Date();

console.log("Slice method:", sliceEnd - sliceStart);
console.log("Apply method:", protoEnd - protoStart);
console.log("For and push method:", pushEnd - pushStart);
console.log("For and batch push method:", batchPushEnd - batchPushStart);
console.log("Foreach and push method:", forEachEnd - forEachStart);

在2014 MacBook Pro 15上运行10,000,000个元素结果:

Slice method: 1400
Apply method: 275
For and push method: 896
For and batch push method: 409
Foreach and push method: 707

for-andachach单推方法在Chrome V8的性能方面大致相当。切片方法我看到的性能差了2-3倍。

更新:为Array.prototype.push.apply添加批处理方法后,它提供了比单推方法更好的性能!然而,展开循环似乎有时会有显着的性能提升,有时则不会,取决于列表的大小以及其之前或之后的其他工作......?

请注意,增加初始x数组的大小会导致Chrome中的页面崩溃,因为它可能会超出页面/标签的最大内存限制。

总结一下,为了简单起见,请坚持使用常规array.push(..),但批量Array.prototype.push.apply方法可能会对性能感兴趣。

答案 1 :(得分:0)

没有选项,因为你有一个可以传递给函数的最大参数长度(not standard defined);

选项是(.apply除外):

1- Spread运算符和.call。同样的错误。

Array.prototype.push.call(a, ...b);

2- Splice(安德鲁)。同样的错误。

a.splice(a.length, 0, ...b); 

由于参数的限制,你不能像concat那样单独迭代来实现这一点。