Толчок Javascript в массив - производительность
Предположим, у нас есть 2 очень больших массива размера N и M,
var array1 = [1,2,3...N],
array2 = [a,b,c...M];
Какой самый оптимальный способ вставить массив2 в массив1?
Используя собственный JavaScript,
Array.prototype.push.apply(array1,array2)
Используя Lodash,
array1 = _.concat(array1, array2)
Поскольку lodash делает копию исходного массива, я думаю, что его сложность возрастет на O(N) по сравнению с нативным JavaScript. Есть ли какой-либо другой оптимизированный способ выдвинуть массив? Кроме того, почему такая популярная библиотека, как lodash, не предоставляет возможность изменить существующий массив для повышения производительности?
3 ответа
Давайте сделаем простой тест и посмотрим. Когда массив, переданный push для применения в качестве аргументов, имеет большой размер, например 150 КБ (в моем текущем случае это происходит после 250076), вы заметите ошибку диапазона, о которой я упоминал в комментариях. Это серьезная проблема, поскольку для небольших массивов разница в производительности в любом случае незначительна. Поэтому следует избегать использования оператора push с применением или распространением, если ваш массив для объединения достаточно велик, чтобы вы беспокоились о производительности операции.
function measureConcatPerformances() {
var len = +arrayLength.value,
ar1 = new Array(len).fill().map(e => ~~(Math.random() * 1000000)),
ar2 = new Array(len).fill().map(e => ~~(Math.random() * 1000000)),
ts = 0,
te = 0;
myError.textContent = "";
ts = performance.now();
for (var i = 0; i < len; i++) ar1[ar1.length] = ar2[i];
te = performance.now();
forLoopResult.textContent = "ar1 and ar2 concataned in: " + (te - ts) + "msec by for loop";
ar1 = new Array(len).fill().map(e => ~~(Math.random() * 1000000));
ar2 = new Array(len).fill().map(e => ~~(Math.random() * 1000000));
ts = performance.now();
for (var val of ar2) ar1[ar1.length] = val;
te = performance.now();
forOfResult.textContent = "ar1 and ar2 concataned in: " + (te - ts) + "msec by for of loop";
ar1 = new Array(len).fill().map(e => ~~(Math.random() * 1000000));
ar2 = new Array(len).fill().map(e => ~~(Math.random() * 1000000));
try{
ts = performance.now();
Array.prototype.push.apply(ar1, ar2);
te = performance.now();
applyResult.textContent = "ar1 and ar2 concataned in: " + (te - ts) + "msec by apply";
} catch(err) {myError.textContent = "Error at apply: " + err}
ar1 = new Array(len).fill().map(e => ~~(Math.random() * 1000000));
ar2 = new Array(len).fill().map(e => ~~(Math.random() * 1000000));
ts = performance.now();
ar1 = ar1.concat(ar2);
te = performance.now();
concatResult.textContent = "ar1 and ar2 concataned in: " + (te - ts) + "msec by concat";
}
myButton.addEventListener("click", measureConcatPerformances);
<input id="arrayLength" type="number" min="0" value = 0>
<button id="myButton">Length</button>
<p id="forLoopResult"></p>
<p id="forOfResult"></p>
<p id="applyResult"></p>
<p id="concatResult"></p>
<p id="myError" style="color:red"></p>
push.apply(array1, array2)
является наиболее оптимальным, так как он изменяет ссылку на array1
, но не копировать / возвращать его для переназначения.
Вы могли бы использовать rest element
собирать элементы массива в другой массив; это собирать array2
в array1
когда оба массива созданы
var array1 = [...[1,2,3] ...(array2 = ["a", "b", "c"])];