Каков самый быстрый способ перебора массива в JavaScript?
Я узнал из книг, что вы должны писать для цикла следующим образом:
for(var i=0, len=arr.length; i < len; i++){
// blah blah
}
поэтому arr.length
не будет рассчитываться каждый раз.
Другие говорят, что компилятор выполнит некоторую оптимизацию, поэтому вы можете просто написать:
for(var i=0; i < arr.length; i++){
// blah blah
}
Я просто хочу знать, какой из них лучше всего на практике?
Ответы
Ответ 1
После выполнения этого теста с большинством современных браузеров...
http://jsben.ch/y3SpC
В настоящее время самая быстрая форма цикла (и, на мой взгляд, наиболее синтаксически очевидная).
стандарт для цикла с кэшированием по длине
for (var i = 0, len = myArray.length; i < len; i++) {
}
Я бы сказал, что это определенно случай, когда я приветствую разработчиков движка JavaScript. Время выполнения должно быть оптимизировано для ясности, а не для удобства.
Ответ 2
Абсолютным самым быстрым способом петли через массив javascript является:
var len = arr.length;
while (len--) {
// blah blah
}
См. http://blogs.oracle.com/greimer/entry/best_way_to_code_a для полного сравнения
Ответ 3
По состоянию на июнь 2016 года, несколько тестов в последнем Chrome (71% рынка браузеров в мае 2016 года и увеличение):
- Самый быстрый цикл - это цикл цикла, как с длиной кеширования, так и без него, что обеспечивает очень похожую производительность. (Цикл for с кэшированной длиной иногда дает лучшие результаты, чем тот, который не кэшируется, но разница почти незначительна, а это значит, что движок может быть оптимизирован в пользу стандартного и, возможно, самого простого цикла без кэширования).
- Цикл while с декрементами был примерно в 1,5 раза медленнее, чем цикл for.
- Цикл с использованием функции обратного вызова (например, standard forEach) был примерно в 10 раз медленнее, чем цикл for.
Я считаю, что этот поток слишком стар, и программисты считают, что им нужно кэшировать длину, или использовать обратные пересечения с уменьшением, чтобы добиться большей производительности, написания кода, который является менее разборчивым и более подверженным ошибкам, чем простой простой для цикла. Поэтому я рекомендую:
-
Если ваше приложение повторяет множество элементов или ваш код цикла находится внутри функции, которая используется часто, прямой цикл является ответом:
for (var i = 0; i < arr.length; i++) {
// Do stuff with arr[i] or i
}
-
Если ваше приложение действительно не перебирает множество элементов или вам просто нужно делать небольшие итерации здесь и там, использование стандартного обратного вызова forEach или любой подобной функции из выбранной вами библиотеки JS может быть более понятным и менее подвержен ошибкам, поскольку область переменных индекса закрыта, и вам не нужно использовать скобки, напрямую обращающиеся к значению массива:
arr.forEach(function(value, index) {
// Do stuff with value or index
});
-
Если вам действительно нужно помять несколько миллисекунд, итерации по миллиардам строк, и длина массива не изменится в процессе, вы можете подумать о кешировании длины в цикле for. Хотя я думаю, что в наши дни это действительно не нужно:
for (var i = 0, len = arr.length; i < len; i++) {
// Do stuff with arr[i]
}
Ответ 4
Если порядок не важен, я предпочитаю этот стиль:
for(var i = array.length; i--; )
Он кэширует длину и намного короче, чтобы писать. Но он будет перебирать массив в обратном порядке.
Ответ 5
Это просто 2018, так что обновление может быть хорошим...
И я действительно должен не согласиться с принятым ответом. Это зависит от разных браузеров. некоторые делают forEach
быстрее, некоторые for-loop
, а некоторые while
тестируют все методы http://jsben.ch/mW36e
arr.forEach( a => {
// ...
}
и так как вы можете увидеть множество циклов for(a = 0;... )
например, for(a = 0;... )
то стоит упомянуть, что без переменных 'var' будет определяться глобально, и это может существенно повлиять на скорость, поэтому она будет медленной.
var arr = arr = new Array(11111111).fill(255);
var benches =
[ [ "empty", () => {
for(var a = 0, l = arr.length; a < l; a++);
}]
, ["for-loop", () => {
for(var a = 0, l = arr.length; a < l; ++a)
var b = arr[a] + 1;
}]
, ["for-loop++", () => {
for(var a = 0, l = arr.length; a < l; a++)
var b = arr[a] + 1;
}]
, ["for-loop - arr.length", () => {
for(var a = 0; a < arr.length; ++a )
var b = arr[a] + 1;
}]
, ["reverse for-loop", () => {
for(var a = arr.length - 1; a >= 0; --a )
var b = arr[a] + 1;
}]
,["while-loop", () => {
var a = 0, l = arr.length;
while( a < l ) {
var b = arr[a] + 1;
++a;
}
}]
, ["reverse-do-while-loop", () => {
var a = arr.length - 1; // CAREFUL
do {
var b = arr[a] + 1;
} while(a--);
}]
, ["forEach", () => {
arr.forEach( a => {
var b = a + 1;
});
}]
, ["for..in (only 3.3%)", () => {
var ar = arr.slice(0,arr.length/33);
for( const a in ar ) {
var b = a + 1;
}
}]
, ["Duff device", () => {
var i = 0;
var r = arr.length % 8;
var n = (arr.length - r) / 8;
if (r > 0) do {
var b = arr[i++] + 1;
}
while (--r);
if (n > 0) do {
var b = arr[i] + 1;
var c = arr[i+1] + 1;
var d = arr[i+2] + 1;
var e = arr[i+3] + 1;
var f = arr[i+4] + 1;
var g = arr[i+5] + 1;
var h = arr[i+6] + 1;
var k = arr[i+7] + 1;
i = --n >>> 3;
}
while (n);
}]
, ["Duff device negative", () => {
var r = arr.length % 8;
var n = (arr.length-r) / 8; ///Math.floor(arr.length / 8);
var i = arr.length ; // -1;
while(r){
var b = arr[--i] + 1;
--r;
}
while(n){
var b = arr[i] + 1;
var c = arr[i-1] + 1;
var d = arr[i-2] + 1;
var e = arr[i-3] + 1;
var f = arr[i-4] + 1;
var g = arr[i-5] + 1;
var h = arr[i-6] + 1;
var j = arr[i-7] + 1;
i = --n >>> 3;
}
}]];
function bench(title, f) {
var t0 = performance.now();
var res = f();
return performance.now() - t0; // console.log('${title} took ${t1-t0} msec');
}
var globalVarTime = bench( "for-loop without 'var'", () => {
// Here if you forget to put 'var' so variables'll be global
for(a = 0, l = arr.length; a < l; ++a)
var b = arr[a] + 1;
});
var times = benches.map( function(a) {
arr = new Array(11111111).fill(255);
return [a[0], bench(...a)]
}).sort( (a,b) => a[1]-b[1] );
var max = times[times.length-1][1];
times = times.map( a => {a[2] = (a[1]/max)*100; return a; } );
var template = (title, time, n) =>
'<div>' +
'<span>${title} </span>' +
'<span style="width:${3+n/2}%"> ${Number(time.toFixed(3))}msec</span>' +
'</div>';
var strRes = times.map( t => template(...t) ).join("\n") +
'<br><br>for-loop without 'var' ${globalVarTime} msec.';
var $container = document.getElementById("container");
$container.innerHTML = strRes;
body { color:#fff; background:#333; font-family:helvetica; }
body > div > div { clear:both }
body > div > div > span {
float:left;
width:43%;
margin:3px 0;
text-align:right;
}
body > div > div > span:nth-child(2) {
text-align:left;
background:darkorange;
animation:showup .37s .111s;
-webkit-animation:showup .37s .111s;
}
@keyframes showup { from { width:0; } }
@-webkit-keyframes showup { from { width:0; } }
<div id="container"> </div>
Ответ 6
2014 While
назад
Подумайте логично.
Посмотрите на это
for( var index = 0 , length = array.length ; index < length ; index++ ) {
//do stuff
}
- Необходимо создать не менее 2 переменных (индекс, длина)
- Необходимо проверить, меньше ли указатель длины
- Необходимо увеличить индекс
- цикл
for
имеет 3 параметра
Теперь скажите мне, почему это должно быть быстрее, чем:
var length = array.length;
while( --length ) { //or length--
//do stuff
}
- Одна переменная
- Нет проверок
- индекс уменьшается (машины предпочитают это)
-
While
имеет только один параметр
Я был полностью сбит с толку, когда Chrome 28 показал, что цикл for работает быстрее, чем время.
Это должно быть как-то вроде
"Ну, каждый использует цикл for, пусть фокусируется на этом, когда
для хром ".
Но теперь, в 2014 году, цикл while возвращается на хром. он в 2 раза быстрее, в других/старых браузерах он всегда был быстрее.
В последнее время я сделал несколько новых тестов. Теперь в реальном мире envoirement эти короткие коды ничего не стоят, и jsperf не может фактически правильно выполнить цикл while, потому что ему нужно воссоздать array.length, что также требует времени.
НЕ МОЖЕТ получить фактическую скорость цикла while на jsperf.
вам нужно создать свою собственную функцию и проверить, что с помощью window.performance.now()
И да... нет никакого способа, чтобы цикл while был быстрее.
Реальная проблема - это фактическое манипулирование/время воспроизведения/ время рисования или, тем не менее, вы хотите называть его.
Например, у меня есть сцена canvas, где мне нужно вычислить координаты и коллизии... это делается между 10-200 MicroSeconds (не миллисекундами). он фактически принимает различные миллисекунды, чтобы сделать все. Так же, как в DOM.
НО
В некоторых случаях существует еще один суперэффективный способ использования loop
... например, для копирования/клонирования массива
for(
var i = array.length ;
i > 0 ;
arrayCopy[ --i ] = array[ i ] // doing stuff
);
Обратите внимание на настройку параметров:
- То же, что и в цикле while. Я использую только одну переменную
- Необходимо проверить, больше ли индекс больше 0;
- Как вы можете видеть, этот подход отличается от обычного для цикла, который каждый использует, поскольку я делаю материал внутри 3-го параметра, а также уменьшаю непосредственно внутри массива.
Сказано, что это подтверждает, что машины, такие как
писал, что я думал, чтобы сделать его немного короче и удалить некоторые бесполезные вещи и написал этот, используя тот же стиль:
for(
var i = array.length ;
i-- ;
arrayCopy[ i ] = array[ i ] // doing stuff
);
Даже если это короче, похоже, что использование i
еще раз замедляет все.
Это на 1/5 медленнее предыдущего цикла for
и While
.
Примечание: ;
очень важно после того, как для looo без {}
Даже если я только что сказал вам, что jsperf - это не лучший способ протестировать скрипты. Я добавил здесь 2 цикла.
http://jsperf.com/caching-array-length/40
И вот еще один ответ о производительности в javascript
fooobar.com/questions/1682/...
Этот ответ должен показать исполнительные способы написания javascript. Поэтому, если вы не можете прочитать это, спросите, и вы получите ответ или прочитаете книгу о javascript http://www.ecma-international.org/ecma-262/5.1/
Ответ 7
http://jsperf.com/caching-array-length/60
Последняя ревизия теста, которую я подготовил (путем повторного использования более старого), показывает одну вещь.
Длина кэширования не так важна, но это не вредит.
Каждый первый запуск теста, связанного выше (на недавно открывшейся вкладке), дает наилучшие результаты для последних 4 фрагментов (3-й, 5-й, 7-й и 10-й в диаграммах) в Chrome, Opera и Firefox на моем 64-битном Debian Squeeze (мое настольное оборудование). Последующие прогоны дают совсем другой результат.
Выводы по производительности просты:
- Перейдите в цикл for (вперед) и проверьте с помощью
!==
вместо <
.
- Если вам не нужно повторно использовать массив позже, тогда также эффективен цикл с уменьшенной длиной и деструктивным массивом
shift()
-ing.
ТЛ; дг
В настоящее время (2011.10) ниже шаблон выглядит как самый быстрый.
for (var i = 0, len = arr.length; i !== len; i++) {
...
}
Помните, что кэширование arr.length
здесь не имеет решающего значения, поэтому вы можете просто протестировать i !== arr.length
, и производительность не снизится, но вы получите более короткий код.
PS: Я знаю, что в фрагменте с shift()
его результат можно использовать вместо доступа к 0-му элементу, но я почему-то упускал из виду, что после повторного использования предыдущей ревизии (которая имела неправильное значение во время циклов), а позже я не хотел теряют уже полученные результаты.
Ответ 8
"Лучший", как в чистом исполнении? или производительность И?
Чистая производительность "наилучшая" - это то, что использует кеш и префиксный оператор ++ (мои данные: http://jsperf.com/caching-array-length/189)
for (var i = 0, len = myArray.length; i < len; ++i) {
// blah blah
}
Я бы сказал, что цикл без кэша - лучший баланс времени выполнения и времени чтения программиста. Каждый программист, начинающийся с C/С++/Java, не будет тратить мс, чтобы прочитать этот
for(var i=0; i < arr.length; i++){
// blah blah
}
Ответ 9
** кешируйте длину массива внутри цикла, некоторые секунды времени будут ускользать. Зависит от элементов в массиве, если в массиве есть больше элементов, существует большая разница относительно Ms времени *
**
sArr; //Array[158];
for(var i = 0 ; i <sArr.length ; i++) {
callArray(sArr[i]); //function call
}
***end: 6.875ms***
**
**
sArr; //Array[158];
for(var i = 0,len = sArr.length ; i < len ; i++) {
callArray(sArr[i]); //function call
}
***end: 1.354ms***
**
Ответ 10
Этот выглядит как самый быстрый способ.
var el;
while (el = arr.shift()) {
el *= 2;
}
Учтите, что это поглотит массив, поедает его и не оставляет ничего...
Ответ 11
Это год 2017.
Я сделал несколько тестов.
https://jsperf.com/fastest-way-to-iterate-through-an-array/
Похоже, метод while
является самым быстрым в Chrome.
Похоже, что левый декремент (--i
) намного быстрее остальных (++i
, i--
, i++
) в Firefox.
Этот подход является постом в среднем. Но он выполняет итерацию массива в обратном порядке.
let i = array.length;
while (--i >= 0) {
doSomething(array[i]);
}
Если приоритетный порядок важен, используйте этот подход.
let ii = array.length;
let i = 0;
while (i < ii) {
doSomething(array[i]);
++i;
}
Ответ 12
Я всегда пишу в первом стиле.
Даже если компилятор достаточно умен, чтобы оптимизировать его для массивов, но все же он умный, если мы используем DOMNodeList здесь или какой-то сложный объект с рассчитанной длиной?
Я знаю, что вопрос о массивах, но я считаю хорошей практикой писать все ваши петли в одном стиле.
Ответ 13
var arr = []; // The array
var i = 0;
while (i < arr.length) {
// Do something with arr[i]
i++;
}
i ++ быстрее, чем ++ i, --i и я -
Кроме того, вы можете сохранить последнюю строку, сделав arr [i ++] в последний раз, когда вам нужно получить доступ к я (но это может быть сложно отлаживать).
Вы можете протестировать его здесь (с другими тестами цикла): http://jsperf.com/for-vs-whilepop/5
Ответ 14
Самое элегантное решение, о котором я знаю, это использование карты.
var arr = [1,2,3];
arr.map(function(input){console.log(input);});
Ответ 15
Я попробовал другие способы перебора огромного массива и выяснил, что сокращение длины массива вдвое, а затем повторение обеих половин в одном цикле происходит быстрее. Эта разница в производительности может быть видна при обработке огромных массивов.
var firstHalfLen =0;
var secondHalfLen = 0;
var count2=0;
var searchterm = "face";
var halfLen = arrayLength/2;
if(arrayLength%2==halfLen)
{
firstHalfLen = Math.ceil(halfLen);
secondHalfLen=Math.floor(halfLen);
}
else
{
firstHalfLen=halfLen;
secondHalfLen=halfLen;
}
for(var firstHalfCOunter=0,secondHalfCounter = arrayLength-secondHalfLen;
firstHalfCOunter < firstHalfLen;
firstHalfCOunter++)
{
if(mainArray[firstHalfCOunter].search(new RegExp(searchterm, "i"))> -1)
{
count2+=1;
}
if(secondHalfCounter < arrayLength)
{
if(mainArray[secondHalfCounter].search(new RegExp(searchterm, "i"))> -1)
{
count2+=1;
}
secondHalfCounter++;
}
}
Некоторое сравнение производительности (используя timer.js) между кэшированной длиной для цикла VS указанным выше способом.
http://jsfiddle.net/tejzpr/bbLgzxgo/
Ответ 16
Другой тест jsperf.com: http://jsperf.com/while-reverse-vs-for-cached-length
Обратный цикл while кажется самым быстрым. Единственная проблема заключается в том, что while (-i) остановится на 0. Как я могу получить доступ к массиву [0] в моем цикле?
Ответ 17
По состоянию на сентябрь 2017 года эти тесты jsperf показывают, что следующий шаблон наиболее эффективен в Chrome 60:
function foo(x) {
x;
};
arr.forEach(foo);
Может ли кто-нибудь воспроизвести?
Ответ 18
Основной цикл while часто является самым быстрым. jsperf.com - отличная песочница для тестирования этих типов концепций.
https://jsperf.com/fastest-array-loops-in-javascript/24
Ответ 19
Хотя цикл немного быстрее, чем цикл.
var len = arr.length;
while (len--) {
// blah blah
}
Вместо этого используйте while loop
Ответ 20
Попробуй это:
var myarray =[],
i = myarray.lenght;
while(i--){
// do somthing
}
Ответ 21
По состоянию на 2019 год WebWorker стал более популярным, для больших наборов данных мы можем использовать WebWorker для гораздо более быстрой обработки, полностью используя многоядерные процессоры.
У нас также есть Parallel.js, который значительно упрощает использование WebWorker для обработки данных.
Ответ 22
Более быстрый способ циклы в массиве - использовать фильтр. Метод filter() создает новый массив со всеми элементами, которые проходят тест, реализованный предоставленной функцией.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
const words = ['Floccinaucinihilipilification', 'limit', 'elite', 'Hippopotomonstrosesquipedaliophobia', 'destruction', 'present'];
const result = words.filter(word => word.length > 6);
console.log(new Date(), result);