Vous devez parcourir une liste de produits, afficher les valeurs d’un tableau ou traiter chaque objet d’une collection sans gérer vous-même un compteur. La boucle for-each de Java répond précisément à ce besoin, avec une syntaxe plus lisible et moins sujette aux erreurs. Je présente ici sa syntaxe, ses usages sur les tableaux et collections, ses limites, ainsi que les cas où une boucle classique reste préférable.
La boucle for-each simplifie le parcours des données Java
- Syntaxe : une variable reçoit chaque élément de la source, l’un après l’autre.
- Sources compatibles : tableaux et objets qui implémentent Iterable.
- Limite principale : aucun accès direct à l’index courant.
- Modification : changer un objet est possible, supprimer une collection pendant le parcours demande une méthode adaptée.
- Alternative : utilisez la boucle classique lorsque vous avez besoin d’un index, d’un parcours inversé ou d’un contrôle plus précis.
Comprendre la syntaxe for-each en Java
La forme générale est simple. On déclare le type de l’élément, son nom local, puis la collection ou le tableau à parcourir. À chaque tour, Java place l’élément suivant dans cette variable.
for (Type element : source) {
// traitement de element
}
Avec un tableau d’entiers, cela donne un code très lisible. Je préfère cette approche dès que le compteur n’a aucune utilité, car elle évite les erreurs classiques de limite comme un index qui dépasse la taille du tableau.
int[] temperatures = {18, 21, 23, 19};
for (int temperature : temperatures) {
System.out.println(temperature + " °C");
}
La variable temperature reçoit successivement 18, 21, 23 et 19. La boucle s’arrête automatiquement après le dernier élément, sans appel explicite à length ni incrémentation manuelle.
Le fonctionnement avec une liste
La même syntaxe fonctionne avec une collection comme ArrayList. Le type de la variable doit correspondre au type des éléments, ou utiliser un type compatible.
List villes = List.of("Paris", "Lyon", "Nantes");
for (String ville : villes) {
System.out.println(ville);
}
Dans les coulisses, Java utilise un itérateur pour les objets Iterable. Cette abstraction permet de parcourir une liste, un ensemble ou une file sans dépendre de leur organisation interne.
Quels objets peut-on parcourir avec cette boucle
Le cas le plus connu est le tableau, y compris un tableau de types primitifs comme int[]. Les collections standards telles que List, Set et de nombreuses implémentations de Queue sont également compatibles grâce à l’interface Iterable.
| Source | Compatible | Exemple recommandé |
|---|---|---|
| Tableau | Oui | for (int n : nombres) |
| List | Oui | for (String nom : noms) |
| Set | Oui | for (String valeur : valeurs) |
| Map | Pas directement | Parcourir entrySet(), keySet() ou values()
|
| Stream | Pas avec cette syntaxe | Utiliser forEach() ou une opération de stream |
Parcourir une Map correctement
Une Map associe des clés à des valeurs et n’implémente pas directement Iterable. Pour accéder aux deux informations, je recommande généralement entrySet(), qui évite de rechercher chaque valeur à partir de sa clé.
Map stocks = Map.of(
"Clavier", 12,
"Souris", 25
);
for (Map.Entry entree : stocks.entrySet()) {
System.out.println(entree.getKey() + " : " + entree.getValue());
}
Si seules les clés ou les valeurs vous intéressent, utilisez respectivement keySet() ou values(). Ce choix rend l’intention du code plus claire et évite de manipuler des données inutiles.
For-each et forEach ne désignent pas exactement la même chose
La boucle enhanced for et la méthode forEach() sont souvent confondues. Elles servent toutes deux à traiter chaque élément, mais leur syntaxe et leur contrôle d’exécution diffèrent.
| Critère | Boucle enhanced for | Méthode forEach |
|---|---|---|
| Syntaxe | Bloc de boucle classique | Lambda ou référence de méthode |
break et continue
|
Disponibles | Non disponibles directement |
| Lisibilité | Très bonne pour plusieurs instructions | Pratique pour une action courte |
| Index | Non disponible automatiquement | Non disponible automatiquement |
List langages = List.of("Java", "Python", "Go");
langages.forEach(langage -> {
System.out.println(langage);
});
Dans un traitement court, forEach() peut être élégant. Pour une logique avec plusieurs conditions, un break, un continue ou des exceptions contrôlées, je trouve la boucle enhanced for plus claire et plus facile à maintenir.
Modifier les éléments sans provoquer d’erreur
La variable déclarée dans la boucle représente l’élément courant, mais elle ne donne pas toujours le contrôle que l’on imagine. Avec des objets, modifier un champ de l’objet modifie bien cet objet. En revanche, réassigner la variable ne remplace pas l’élément dans la collection.
class Utilisateur {
String nom;
boolean actif;
Utilisateur(String nom, boolean actif) {
this.nom = nom;
this.actif = actif;
}
}
for (Utilisateur utilisateur : utilisateurs) {
utilisateur.actif = true;
}
Ici, le champ actif est modifié sur chaque objet. En revanche, ce code ne remplace pas les objets présents dans la liste.
for (Utilisateur utilisateur : utilisateurs) {
utilisateur = new Utilisateur("Anonyme", true);
}
Cette réaffectation ne change que la variable locale. C’est un détail important, car c’est l’une des confusions que je rencontre le plus souvent chez les débutants.
Supprimer des éléments pendant le parcours
Supprimer directement un élément d’une collection dans une boucle enhanced for peut provoquer une ConcurrentModificationException. La solution dépend du besoin. Pour retirer les éléments qui respectent une condition, removeIf() est souvent le choix le plus propre.
List nombres = new ArrayList<>(
List.of(3, 8, 11, 14, 17)
);
nombres.removeIf(nombre -> nombre % 2 == 0);
Si vous devez supprimer progressivement avec une logique plus complexe, utilisez explicitement un Iterator et sa méthode remove(). Je déconseille de contourner le problème en modifiant la collection depuis une autre partie du programme, car le résultat devient difficile à prévoir.
Quand la boucle classique reste plus adaptée
La boucle for-each est idéale pour lire ou traiter chaque élément. Elle devient moins pratique dès que la position de l’élément compte. Pour accéder à l’index, comparer deux tableaux ou parcourir une liste dans le sens inverse, la boucle classique conserve un avantage net.
String[] fichiers = {"rapport.pdf", "photo.jpg", "notes.txt"};
for (int index = 0; index < fichiers.length; index++) {
System.out.println(index + " : " + fichiers[index]);
}
Avec une liste, une boucle classique permet aussi de récupérer directement l’élément précédent ou suivant. Pour une modification structurelle contrôlée, ListIterator peut être encore plus approprié.
Lire aussi : Getters Java - Maîtrisez les bonnes pratiques et évitez les pièges
Les limites à garder en tête
- La boucle ne fournit aucun index automatique.
- Une référence nulle provoque une NullPointerException au démarrage du parcours.
- L’ordre dépend de la source. Un
HashSetne garantit pas le même ordre qu’une liste. - Modifier la structure d’une collection pendant le parcours peut générer une exception.
- Une boucle for-each ne parcourt pas directement un
Stream.
Sur le plan des performances, il ne faut pas supposer qu’une boucle classique est toujours plus rapide. Pour une collection, l’itérateur est souvent la manière naturelle de parcourir les éléments. Le choix doit surtout venir de la lisibilité et du contrôle nécessaire, sauf si un profilage démontre un véritable problème de performance.
La règle simple pour choisir la bonne boucle
J’utilise la boucle for-each lorsque je veux traiter chaque élément sans me préoccuper de sa position. Elle convient aux tableaux, aux listes, aux ensembles et à la plupart des objets qui implémentent Iterable.
Je passe à la boucle classique lorsque l’index, le sens du parcours ou la comparaison entre plusieurs positions devient important. Pour supprimer selon une condition, removeIf() est généralement plus sûr, tandis que forEach() convient surtout aux actions courtes exprimées avec une lambda.
Retenir cette distinction suffit dans la majorité des projets Java. La meilleure boucle n’est pas celle qui paraît la plus moderne, mais celle qui rend clairement visible ce que le code fait réellement.