Толчок 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"])];
Другие вопросы по тегам