【发布时间】:2020-09-09 22:01:11
【问题描述】:
是否有任何干净有效的方法可以将一个数组的内容直接添加到另一个数组,而无需制作所有数据的中间/临时副本?
例如,您可以使用.push() 将一个数组的内容直接添加到另一个数组中,如下所示:
// imagine these are a both large arrays
let base = [1,2,3];
let data = [4,5,6]
base.push(...data);
但是,这似乎复制了data 中的所有项目,因为它使它们成为.push() 的参数。 .splice() 也是如此:
// imagine these are a both large arrays
let base = [1,2,3];
let data = [4,5,6]
base.splice(base.length, 0, ...data);
从内存的角度(所有数据的额外副本)和执行的角度(数据被迭代两次)的角度来看,这两种方法似乎都是低效的。
.concat() 之类的方法不会将一个数组的内容添加到另一个数组,而是使用组合内容创建一个新数组(复制两个数组的内容)。
我有一些包含大量操作的大型数组,我试图通过避免不必要的中间副本来减轻 CPU/垃圾收集器的负担,我很奇怪我还没有找到这样的内置-在操作中。到目前为止,我避免不必要的副本的最佳选择是:
// imagine these are a both large arrays
let base = [1,2,3];
let data = [4,5,6];
for (let item of data) {
base.push(item);
}
如果它是一个操作,显然它的执行效率可能不如它可能是一个操作,而且当你希望它是一行时,它显然是多行代码。
【问题讨论】:
-
AFAIK 如果你想得到一个数组,它永远不会是一个操作。如果您想在合并中进行一项操作,则可以使用自定义数据结构。像 2d 数组,但对其进行迭代会很复杂。即使拼接和展开运算符也不是“一次操作”,即使它们使用了这么多额外空间
-
浏览器可能会优化
base.push(...data)(现在或将来的版本)。参见例如 dev.to/uilicious/…、stackoverflow.com/questions/5080028/… 和 dmitripavlutin.com/… -
@SebastianB。 - 问题是,如果它是一个大数组,并且您尝试在未优化它的环境中运行它,则会出现堆栈溢出。因此,“可能会得到优化”并不能很好地工作。有趣的是,它可以得到优化。我只需要一种始终优化的方法。您的第二个参考在其中一个答案中提到了堆栈溢出问题。所以,我不能以一种可能做到这一点的方式编码。
-
我明白了,这是一个有趣的问题。但我担心在这种高度解释的语言中手动优化也是危险的。如果你把它推到极限,今天有效的东西明天可能就没有那么高效了。使用typed arrays 是一种选择吗?或者那部分的 WebAssembly (developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…)。只是在没有自己使用这些 API 的情况下进行头脑风暴......
-
@SebastianB。 - 我了解在移动目标上调整优化的危险。但是,我必须通过将一个巨大的数组推到堆栈上来避免可能导致堆栈爆炸的结构,所以我不能真正使用它,除非我的代码只针对一个非常特定的 JS 引擎,该引擎已知对我的特定条件是安全的,比如 nodejs V12+ 然后它假设 nodejs 中的 JS 引擎永远不会倒退。这就是我当前的实现在我的问题中使用
for循环的原因。至少它在所有引擎中都是安全的。我对 webAssembly 一无所知 - 也许是时候学习了。
标签: javascript arrays append