例如,我有两个非常大的数组a
和b
,每个都有数百万个元素。我想将数组b
的所有元素追加到数组a
。此外,我不想创建新数组,但更改现有数组a
(因此concat
不是选项)。
我试过了:
Array.prototype.push.apply(a, b)
但对于非常大的数组,这给了我一个错误:
RangeError: Maximum call stack size exceeded
我知道我可以制作循环并使用push
逐个添加元素。
有更好的方法吗?
答案 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那样单独迭代来实现这一点。