web-dev-qa-db-fra.com

Quel est le moyen le plus rapide de parcourir un tableau en JavaScript?

J'ai appris de livres que vous devriez écrire pour la boucle comme ceci:

for(var i=0, len=arr.length; i < len; i++){
    // blah blah
}

de sorte que le arr.length ne sera pas calculé à chaque fois.

D'autres disent que le compilateur va optimiser certaines choses, vous pouvez donc écrire:

for(var i=0; i < arr.length; i++){
    // blah blah
}

Je veux juste savoir quelle est la meilleure façon de faire?

223
wong2

Après avoir effectué ce test avec la plupart des navigateurs modernes ...

http://jsben.ch/y3SpC

Actuellement , la forme de boucle la plus rapide (et à mon avis la plus évidente du point de vue de la syntaxe).

une norme pour la mise en cache avec longueur

for (var i = 0, len = myArray.length; i < len; i++) {

}

Je dirais que c’est définitivement un cas où j’applaudis aux développeurs de moteurs JavaScript. Un temps d'exécution doit être optimisé pour clarté , et non pas habile .

313
jondavidjohn

Le moyen le plus rapide de parcourir un tableau javascript est le suivant:

var len = arr.length;
while (len--) {
    // blah blah
}

Voir http://blogs.Oracle.com/greimer/entry/best_way_to_code_a pour une comparaison complète.

83
gnur

À compter de juin 2016, effectuant quelques tests dans le dernier Chrome (71% du marché des navigateurs en mai 2016 et en augmentation):

  • La boucle la plus rapide est une boucle for, avec ou sans longueur de cache offrant des performances très similaires. (La boucle for avec une longueur en cache donnait parfois de meilleurs résultats que celle sans cache, mais la différence est presque négligeable, ce qui signifie que le moteur peut déjà être optimisé pour privilégier la boucle standard et probablement la plus simple sans cache).
  • La boucle while avec décréments était environ 1,5 fois plus lente que la boucle for.
  • Une boucle utilisant une fonction de rappel (comme la norme forEach) était environ 10 fois plus lente que la boucle for.

Je crois que ce fil est trop ancien et il est trompeur pour les programmeurs de penser qu'ils ont besoin de mettre en cache la longueur ou d'utiliser des passes inverses avec des diminutions pour obtenir de meilleures performances, en écrivant du code moins lisible et plus sujet aux erreurs qu'une simple boucle for simple. Par conséquent, je recommande:

  • Si votre application effectue une itération sur un grand nombre d'éléments ou si votre code de boucle est contenu dans une fonction utilisée fréquemment, une simple boucle for est la solution:

    for (var i = 0; i < arr.length; i++) {
      // Do stuff with arr[i] or i
    }
    
  • Si votre application ne parcoure pas vraiment de nombreux éléments ou si vous devez simplement faire de petites itérations ici et là, utiliser le standard forChaque rappel ou toute fonction similaire de la bibliothèque JS de votre choix peut être plus compréhensible et moins sujet aux erreurs, car la variable d'index scope est fermée et vous n'avez pas besoin d'utiliser des crochets pour accéder directement à la valeur du tableau:

    arr.forEach(function(value, index) {
      // Do stuff with value or index
    });
    
  • Si vous avez vraiment besoin de parcourir quelques millisecondes tout en parcourant des milliards de lignes et que la longueur de votre tableau ne change pas dans le processus, vous pouvez envisager de mettre en cache la longueur dans votre boucle for. Bien que je pense que ce n'est vraiment pas nécessaire de nos jours:

    for (var i = 0, len = arr.length; i < len; i++) {
      // Do stuff with arr[i]
    }
    
34
CGodo

Si la commande n'est pas importante, je préfère ce style:

for(var i = array.length; i--; )

Il cache la longueur et est beaucoup plus court à écrire. Mais il va parcourir le tableau dans l'ordre inverse.

28
Felix Kling

Nous sommes en 2018, alors une mise à jour pourrait être Nice ...

Et je dois vraiment être en désaccord avec la réponse acceptée . Il diffère sur différents navigateurs. certains font forEach plus vite, certains for-loop, et certains while voici un repère sur toutes les méthodes http://jsben.ch/mW36e

arr.forEach( a => {
  // ...
}

et puisque vous pouvez voir beaucoup de boucles for-like telles que for(a = 0; ... ), il est utile de mentionner que sans variables 'var', elles seront définies globalement et que cela peut considérablement affecter la vitesse, de sorte que la vitesse sera lente.

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's 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's 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} &nbsp;</span>` +
    `<span style="width:${3+n/2}%">&nbsp;${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>
23
nullqube

2014 While est de retour

Pensez simplement logique.

Regardez ceci

for( var index = 0 , length = array.length ; index < length ; index++ ) {

 //do stuff

}
  1. Besoin de créer au moins 2 variables (index, longueur)
  2. Besoin de vérifier si l'index est plus petit que la longueur
  3. Besoin d'augmenter l'index
  4. la boucle for a 3 paramètres

Maintenant, dites-moi pourquoi cela devrait être plus rapide que:

var length = array.length;

while( --length ) { //or length--

 //do stuff

}
  1. Une variable
  2. Pas de chèques
  3. l'indice est diminué (les machines préfèrent cela)
  4. while n'a qu'un paramètre

J'étais totalement dérouté lorsque Chrome 28 a montré que la boucle for est plus rapide que le moment. Cela doit avoir une sorte de

"Euh, tout le monde utilise la boucle for, concentrons-nous là-dessus lorsque vous développez pour Chrome."

Mais maintenant, en 2014, la boucle While est de retour sur Chrome. c'est 2 fois plus rapide, sur les autres/anciens navigateurs, c'était toujours plus rapide.

Dernièrement, j'ai fait de nouveaux tests. Or, dans le monde réel, ces codes abrégés ne valent absolument rien et jsperf ne peut pas exécuter correctement la boucle while, car il faut recréer le tableau array.length, qui prend également du temps.

vous ne pouvez pas obtenir la vitesse réelle d'une boucle while sur jsperf.

vous devez créer votre propre fonction personnalisée et vérifier cela avec window.performance.now()

Et oui ... il n’est pas possible que la boucle while soit simplement plus rapide.

Le vrai problème est en fait la manipulation/le rendu du temps/le temps du dessin ou comment vous voulez l'appeler.

Par exemple, j'ai une scène de toile où j'ai besoin de calculer les coordonnées et les collisions ... cela se fait entre 10-200 MicroSecondes (pas millisecondes). il faut en fait plusieurs millisecondes pour tout restituer. Même chose que dans DOM.

MAIS

Il existe un autre moyen très performant d'utiliser le for loop dans certains cas ... par exemple pour copier/cloner un tableau

for(
 var i = array.length ;
 i > 0 ;
 arrayCopy[ --i ] = array[ i ] // doing stuff
);

Notez la configuration des paramètres:

  1. Comme dans la boucle while j'utilise une seule variable
  2. Besoin de vérifier si l'index est plus grand que 0;
  3. Comme vous pouvez le constater, cette approche est différente de la normale pour la boucle utilisée par tout le monde, comme dans le troisième paramètre et que je diminue également directement dans le tableau.

Cela dit, cela confirme que des machines comme le -

en écrivant que je pensais le raccourcir un peu, enlever quelques trucs inutiles et écrire celui-ci en utilisant le même style:

for(
 var i = array.length ;
 i-- ;
 arrayCopy[ i ] = array[ i ] // doing stuff
);

Même s'il est plus court, l'utilisation de i semble une nouvelle fois ralentie. Elle est 1/5 plus lente que la précédente boucle for et la boucle while.

Remarque: le ; est très important après le for looo sans {}

Même si je viens de vous dire que jsperf n’est pas le meilleur moyen de tester des scripts .. j’ai ajouté ces 2 boucles ici

http://jsperf.com/caching-array-length/4

Et voici une autre réponse à propos des performances en javascript

https://stackoverflow.com/a/21353032/24507

Cette réponse montre les méthodes les plus performantes pour écrire du javascript. Donc si vous ne pouvez pas lire cela, demandez et vous obtiendrez une réponse ou lirez un livre sur le javascript http://www.ecma-international.org/ecma-262/5.1/

19
cocco

http://jsperf.com/caching-array-length/6

La dernière révision du test, que j'ai préparée (en réutilisant l'ancienne), montre une chose.

La longueur du cache n’est pas très importante, mais elle ne nuit pas.

Chaque premier passage du test lié ci-dessus (sur l'onglet récemment ouvert) donne les meilleurs résultats pour les 4 derniers extraits (3ème, 5ème, 7ème et 10ème dans les graphiques) en Chrome, Opera et Firefox dans ma Debian Squeeze 64 -bit ( mon matériel de burea ). Les courses suivantes donnent un résultat assez différent.

Les conclusions en termes de performances sont simples:

  • Utilisez la boucle for (avance) et effectuez un test avec !== au lieu de <.
  • Si vous n'avez pas à réutiliser le tableau plus tard, la boucle while sur la longueur décrémentée et le tableau destructif shift()- sont également efficaces.

tl; dr

De nos jours (2011.10), le motif ci-dessous semble être le plus rapide.

for (var i = 0, len = arr.length; i !== len; i++) {
    ...
}

Notez que la mise en cache de arr.length n'est pas cruciale ici, vous pouvez donc simplement tester i !== arr.length et les performances ne baisseront pas, mais votre code sera plus court.


PS: Je sais que dans l'extrait avec shift(), son résultat pourrait être utilisé au lieu d'accéder à l'élément 0th, mais j'ai oublié de voir qu'après avoir réutilisé la révision précédente (qui avait mal tourné), et plus tard, je ne voulais pas perdre résultats déjà obtenus.

11
przemoc

"Best" comme dans la performance pure? ou performance ET lisibilité?

La performance pure "meilleure" est la suivante: elle utilise un cache et l'opérateur de préfixe ++ (mes données: http://jsperf.com/caching-array-length/189 )

for (var i = 0, len = myArray.length; i < len; ++i) {
  // blah blah
}

Je dirais que la boucle sans cache pour la boucle est le meilleur équilibre entre le temps d'exécution et le temps de lecture du programmeur. Tous les programmeurs ayant commencé avec C/C++/Java ne perdront pas une ms à lire celui-ci

for(var i=0; i < arr.length; i++){
  // blah blah
}
8
valbaca

** cache la longueur du tableau à l'intérieur de la boucle, quelques secondes s'écouleront. Dépend des éléments du tableau s'il y a plus d'éléments dans le tableau, il existe une différence majeure en ce qui concerne MS of time *

**

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***

**

7
Shushanth Pallegar

Ceci semble être le moyen le plus rapide de loin ...

var el;
while (el = arr.shift()) {
  el *= 2;
}

Prenez en compte que cela va consommer le tableau, le manger et ne rien laisser ...

6
Sergio

C'est l'année 2017 .

J'ai fait des tests.

https://jsperf.com/fastest-way-to-iterate-through-an-array/

On dirait que la méthode while est la plus rapide sur Chrome.

On dirait que le décrément gauche (--i) est beaucoup plus rapide que les autres (++i, i--, i++) sur Firefox.

Cette approche est le à jeun en moyenne. Mais cela itère le tableau dans l'ordre inverse.

let i = array.length;
while (--i >= 0) {
    doSomething(array[i]);
}

Si la commande à terme est importante, utilisez cette approche.

let ii = array.length;
let i = 0;
while (i < ii) {
    doSomething(array[i]);
    ++i;
}
4
SeregPie

J'écris toujours dans le premier style.

Même si un compilateur est assez intelligent pour l'optimiser pour les tableaux, il l'est tout de même si nous utilisons DOMNodeList ici ou un objet compliqué avec une longueur calculée?

Je sais quelle est la question sur les tableaux, mais je pense que c'est une bonne pratique d'écrire toutes vos boucles dans un style.

2
Olegas
var arr = []; // The array
var i = 0;
while (i < arr.length) {
    // Do something with arr[i]
    i++;
}

i ++ est plus rapide que ++ i, --i et i--

En outre, vous pouvez enregistrer la dernière ligne en faisant arr [i ++] la dernière fois que vous devez accéder à i (mais cela peut être difficile à déboguer).

Vous pouvez le tester ici (avec d’autres tests de boucle): http://jsperf.com/for-vs-whilepop/5

1
Forestrf

J'ai essayé d'autres manières d'itérer un grand tableau et j'ai découvert que réduire de moitié la longueur du tableau, puis itérer les deux moitiés d'une seule boucle est plus rapide. Cette différence de performance est visible lors du traitement d’énormes tableaux .

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++; 
  }
}

Quelques comparaisons de performances (en utilisant timer.js) entre la longueur mise en cache pour la boucle et la méthode ci-dessus.

http://jsfiddle.net/tejzpr/bbLgzxgo/

0
tejzpr

La solution la plus élégante que je connaisse consiste à utiliser la carte.

var arr = [1,2,3];
arr.map(function(input){console.log(input);});
0
Dan

La boucle While est un peu plus rapide que pour la boucle.

var len = arr.length;
while (len--) {
    // blah blah
}

Utilisez la boucle while à la place

0
Azhar Zafar

Une boucle while de base est souvent la plus rapide. jsperf.com est un excellent bac à sable pour tester ces types de concepts.

https://jsperf.com/fastest-array-loops-in-javascript/24

0
Devappended

Un autre test de jsperf.com: http://jsperf.com/while-reverse-vs-for-cached-length

La boucle reverse while semble être la plus rapide. Le seul problème est que while (--i) va s'arrêter à 0. Comment puis-je accéder à array [0] dans ma boucle alors?

Depuis septembre 2017 ces tests jsperf affichent le schéma suivant comme étant le plus performant sur Chrome 60:

function foo(x) {
 x;
};
arr.forEach(foo);

Est-ce que quelqu'un est capable de se reproduire?

0
John Vandivier

Essaye ça:

var myarray =[],
i = myarray.lenght;
while(i--){
// do somthing
}
0
li bing zhao

Depuis 2019, WebWorker est devenu plus populaire. Pour les grands ensembles de données, nous pouvons utiliser WebWorker pour traiter beaucoup plus rapidement en utilisant pleinement les processeurs multicœurs.

Nous avons également Parallel.js , ce qui rend WebWorker beaucoup plus facile à utiliser pour le traitement des données.

0
Stackia