web-dev-qa-db-fra.com

pourquoi la boucle for for améliorée est-elle plus efficace que la boucle for normale

J'ai lu que la boucle améliorée pour est plus efficace que la boucle normale pour ici:

http://developer.Android.com/guide/practices/performance.html#foreach

Quand j'ai cherché la différence entre leur efficacité, tout ce que j'ai trouvé est: En cas de boucle normale, nous avons besoin d'une étape supplémentaire pour déterminer la longueur du tableau, sa taille, etc.

for(Integer i : list){
   ....
}


int n = list.size();
for(int i=0; i < n; ++i){
  ....
}

Mais est-ce la seule raison pour laquelle la boucle for améliorée est meilleure que la boucle for normale? Dans ce cas, il vaut mieux utiliser la boucle normale pour en raison de la légère complexité liée à la compréhension de la boucle améliorée pour.

Cochez cette case pour un numéro intéressant: http://www.coderanch.com/t/258147/Java-programmer-SCJP/certification/Enhanced-Loop-Vs-Loop

Quelqu'un peut-il expliquer la mise en œuvre interne de ces deux types de boucles for, ou expliquer d'autres raisons d'utiliser la boucle améliorée for?

23
Archie.bpgc

C'est un peu simpliste de dire que la boucle for améliorée est plus efficace. C'est peut être, mais dans de nombreux cas, c'est presque exactement la même chose qu'une boucle à l'ancienne.

La première chose à noter est que pour les collections, la boucle for améliorée utilise une Iterator. Par conséquent, si vous effectuez une itération manuelle sur une collection à l'aide d'une Iterator, vous devriez obtenir à peu près les mêmes performances que la boucle for améliorée.

Un endroit où la boucle for améliorée est plus rapide qu'une boucle traditionnelle mise en œuvre naïvement ressemble à ceci:

LinkedList<Object> list = ...;

// Loop 1:
int size = list.size();
for (int i = 0; i<size; i++) {
   Object o = list.get(i);
   /// do stuff
}

// Loop 2:
for (Object o : list) {
  // do stuff
}

// Loop 3:
Iterator<Object> it = list.iterator();
while (it.hasNext()) {
  Object o = it.next();
  // do stuff
}

Dans ce cas, la boucle 1 sera plus lente que les boucles 2 et 3, car elle devra parcourir (partiellement) la liste à chaque itération pour trouver l'élément à la position i. Les boucles 2 et 3 ne feront cependant qu’avancer d’un élément de la liste, en raison de l’utilisation de Iterator. Les boucles 2 et 3 auront également à peu près les mêmes performances puisque Loop 3 est à peu près exactement ce que le compilateur produira lorsque vous écrivez le code dans Loop 2.

50
Joachim Sauer

Je suis moi-même surpris dans une petite expérience que j'ai faite aujourd'hui avec les points mentionnés ci-dessus . pour la boucle 2) en utilisant Iterator 3) en utilisant une boucle simple et get ()
Je suppose que les programmeurs tels que vous peuvent mieux comprendre ce que j’ai fait en voyant le code.

long advanced_timeElapsed,iterating_timeElapsed,simple_timeElapsed;
    long first=System.nanoTime();
    for(Integer i: myList){
        Integer b=i;
    }
    long end= System.nanoTime();
    advanced_timeElapsed=end-first;
    System.out.println("Time for Advanced for loop:"+advanced_timeElapsed);
    first=System.nanoTime();
    Iterator<Integer> it = myList.iterator();
    while(it.hasNext())
    {
        Integer b=it.next();
    }
    end= System.nanoTime();
    iterating_timeElapsed=end-first;
    System.out.println("Time for Iterating Loop:"+iterating_timeElapsed);
    first=System.nanoTime();
    int counter=0;
    int size= myList.size();
    while(counter<size)
    {
        Integer b=myList.get(counter);
        counter++;  
    }
    end= System.nanoTime();
    simple_timeElapsed=end-first;
    System.out.println("Time for Simple Loop:"+simple_timeElapsed);

Les résultats ne correspondaient pas à ce à quoi je m'attendais. Voici le graphique du temps écoulé dans 3 cas .Time Elapsed Graph

Temps de l'axe des Y écoulé Cas de test de l'axe des X
test case1: 10 entrées
test case2: 30 entrées
test case3: 50 entrées
test case4: 100 entrées
test case5: 150 entrées
test case6: 300 entrées
test case7: 500 entrées
test case8: 1000 entrées
test case9: 2000 entrées
test case10: 5000 entrées
test case11: 10000 entrées
test case12: 100000 entrées

Ici, vous pouvez voir que la boucle simple fonctionne bien mieux que d’autres. Si vous trouvez des erreurs dans le code ci-dessus, répondez et je vérifierai à nouveau. Je ferai le point sur ce sujet après avoir fouillé dans le bytecode et vu ce qui se passe sous le capot.

8
Philip George

J'ai lu que amélioré pour la boucle est efficace que la normale pour la boucle.

En fait, parfois, c'est moins efficace pour le programme, mais la plupart du temps, c'est exactement la même chose.

Son plus efficace pour le développeur, qui est souvent beaucoup plus important

Une boucle for-each est particulièrement utile lors d'une itération sur une collection.

List<String> list = 
for(Iterator<String> iter = list.iterator(); list.hasNext(); ) {
    String s = list.next();

est plus facile à écrire que (mais fait la même chose que, donc ce n'est pas plus efficace pour le programme)

List<String> list = 
for(String s: list) {

L'utilisation de la "vieille" boucle est légèrement plus efficace lorsque vous accédez à une collection accessible de manière aléatoire, par index.

List<String> list = new ArrayList<String>(); // implements RandomAccess
for(int i=0, len = list.size(); i < len; i++) // doesn't use an Iterator!

L'utilisation d'une boucle for-each sur une collection utilise toujours un Iterator qui est légèrement moins efficace pour les listes à accès aléatoire.

D'après les informations dont je dispose, utiliser une boucle for-each n'est jamais plus efficace pour le programme, mais comme je l'ai dit, l'efficacité du développeur est souvent bien plus importante .

7
Peter Lawrey

For-each utilise l'interface Iterator. Je doute que ce soit plus efficace que le "vieux" style. L'Iterator doit également vérifier la taille de la liste.

C'est surtout pour la lisibilité. 

Cela devrait être plus rapide pour les collections à accès non aléatoire telles que LinkedList, mais la comparaison est alors injuste. De toute façon, vous n’auriez de toute façon pas eu recours à une seconde implémentation (avec un accès indexé lent). 

3
Thilo

La boucle foreach est aussi efficace que ce type de boucle:

for (Iterator<Foo> it = list.iterator(); it.hasNext(); ) {
     Foo foo = it.next();
     ...
}

parce que c'est strictement équivalent.

Si vous parcourez une liste en utilisant

int size = list.size();
for (int i = 0; i < size; i++) {
    Foo foo = list.get(i);
    ...
}

La performance de la boucle foreach sera alors équivalente à celle de votre boucle, mais uniquement pour ArrayList . Dans le cas d'une liste chaînée, votre boucle aura des performances abyssales, car à chaque itération, elle devra traverser tous les nœuds de la liste jusqu'à atteindre l'élément ith. 

La boucle foreach (ou la boucle basée sur un itérateur, qui est identique) n'a pas ce problème, car l'itérateur conserve une référence au nœud actuel et passe simplement au suivant à chaque itération. C’est un choix judicieux, car cela fonctionne très bien avec tous les types de listes. Cela exprime également l'intention plus clairement et est plus sûr car vous ne risquez pas d'incrémenter l'index dans la boucle, ni d'utiliser le mauvais index en cas de boucles imbriquées.

3
JB Nizet

toutes les réponses sont bonnes, et je pense qu'il n'en faut plus, mais je tiens à souligner que:

L’instruction for améliorée peut être utilisée uniquement à obtain array elements

il ne peut pas être utilisé à modify elements.

Si votre programme doit modifier des éléments, utilisez l'instruction traditionnelle contrée par un contrôlefor.

regarde 

int[] array = { 1, 2, 3, 4, 5 };
for (int counter = 0; counter < array.length; counter++)
    array[counter] *= 2;

Nous n'avons pas pu utiliser l'instruction enhanced for car nous sommes modifying the array’s elements.

0
Basheer AL-MOMANI

De Java efficace:

L’idiome standard pour la boucle dans un tableau n’entraîne pas nécessairement chèques redondants. Les implémentations modernes de JVM les optimisent.

Mais Josh Bloch ne décrit pas comment JVM les optimise.

0
Geek