Comment fonctionne for-each en Java : analyse avec des exemples du débutant au pro

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Introduction : pourquoi for-each est devenu le "roi" des boucles en Java ?

\n\n

Si vous avez programmé en Java ne serait-ce qu'une semaine, vous avez forcément rencontré la construction for (Type var : collection). Cette syntaxe concise est appelée boucle for-each (ou enhanced for loop). Elle est apparue avec Java 5 (2004) et est rapidement devenue la préférée des développeurs.

\n\n

Mais savez-vous ce qui se passe sous le capot lorsque la JVM rencontre cette construction ? Pourquoi for-each peut-il être plus lent qu'un for classique pour les tableaux ? Et pourquoi lève-t-il parfois une ConcurrentModificationException, même si vous ne supprimez rien ?

\n\n

Dans cet article, nous analyserons comment fonctionne for-each en Java au niveau du bytecode, le comparerons aux boucles classiques et apprendrons à éviter les pièges typiques. C'est parti !

\n\n

Syntaxe et première approche

\n\n

La syntaxe de base est la suivante :

\n\n
for (TypeElement variable : collection_ou_tableau) {\n    // corps de la boucle\n}
\n\n

Exemple avec ArrayList :

\n\n
List<String> noms = Arrays.asList("Anna", "Boris", "Victor");\n\nfor (String nom : noms) {\n    System.out.println("Bonjour, " + nom + " !");\n}
\n\n

Sortie :

\n\n
Bonjour, Anna !\nBonjour, Boris !\nBonjour, Victor !
\n\n

Tout simple ? Oui. Mais derrière cette simplicité se cachent deux mécanismes totalement différents — pour les tableaux et pour les collections.

\n\n

Comment for-each fonctionne-t-il avec les tableaux ?

\n\n

Lorsque vous écrivez for-each pour un tableau, le compilateur Java le transforme en un simple for avec un index. Pas de magie.

\n\n

Votre code :

\n\n
int[] nombres = {10, 20, 30};\n\nfor (int num : nombres) {\n    System.out.println(num);\n}
\n\n

Après compilation (décompilez le bytecode — vérifiez) :

\n\n
int[] nombres = new int[]{10, 20, 30};\nfor (int i = 0; i < nombres.length; i++) {\n    int num = nombres[i];\n    System.out.println(num);\n}
\n\n

Conclusion importante : pour les tableaux, for-each et le for classique sont identiques en termes de performances. Aucun surcoût lié aux itérateurs.

\n\n

Comment for-each fonctionne-t-il avec les collections (List, Set) ?

\n\n

C'est là que ça devient intéressant. Pour les collections, for-each utilise un itérateur.

\n\n

Votre code :

\n\n
List<String> liste = new ArrayList<>();\nliste.add("Java");\nliste.add("Python");\nliste.add("C++");\n\nfor (String lang : liste) {\n    System.out.println(lang);\n}
\n\n

Le compilateur transforme cela en :

\n\n
List<String> liste = new ArrayList<>();\nliste.add("Java");\nliste.add("Python");\nliste.add("C++");\n\nfor (Iterator<String> it = liste.iterator(); it.hasNext(); ) {\n    String lang = it.next();\n    System.out.println(lang);\n}
\n\n

Autrement dit :

\n
    \n
  • liste.iterator() est appelé — on obtient un itérateur.
  • \n
  • La condition est it.hasNext().
  • \n
  • Dans le corps, on a it.next().
  • \n
  • La variable lang est initialisée avec le résultat de next().
  • \n
\n\n

Qu'est-ce que cela signifie en pratique ?

\n
    \n
  • On ne peut pas supprimer d'éléments dans un for-each avec liste.remove() — vous obtiendrez une ConcurrentModificationException. Utilisez it.remove() ou un for classique.
  • \n
  • On ne peut pas modifier la collection (ajouter/supprimer) pendant l'itération — même erreur.
  • \n
  • Performances : pour ArrayList, la différence avec un for classique est minime (création d'un objet itérateur). Pour LinkedList, for-each est beaucoup plus rapide que for (int i = 0; i < liste.size(); i++), car get(i) sur LinkedList est en O(n) à chaque appel !
  • \n
\n\n

Et pour Map ? Itération sur les paires clé-valeur

\n\n

Map n'implémente pas directement Iterable, donc for-each

Blogs

Book Recommendations