Implémenter un itérateur personnalisé
Créez une classe d’itérateur personnalisée pour une liste chaînée simple ou une structure représentant un intervalle.
Implémenter un itérateur personnalisé est une leçon Java Academy gratuite sur CoddyKit. Ceci est la leçon 2 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Java Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Java Academy comprend 4 leçons au total.
Iterator personnalisé
Créer un iterator personnalisé vous donne un contrôle total sur la manière de parcourir une structure de données. Cette leçon présente étape par étape l’implémentation d’un iterator de liste chaînée.
La classe Node
Commencez par définir la structure des nœuds d’une liste simplement chaînée.
class Node<T> {
final T value;
Node<T> next;
Node(T value) {
this.value = value;
}
}
// Building a chain: 1 -> 2 -> 3
Node<Integer> head = new Node<>(1);
head.next = new Node<>(2);
head.next.next = new Node<>(3);Implémentation de l’Iterator
Créez une classe interne qui implémente Iterator<T>, avec un curseur pointant vers le nœud actuel.
import java.util.Iterator;
import java.util.NoSuchElementException;
class LinkedList<T> implements Iterable<T> {
private Node<T> head;
private int size;
private class LinkedListIterator implements Iterator<T> {
private Node<T> current = head; // cursor
@Override
public boolean hasNext() {
return current != null;
}
@Override
public T next() {
if (!hasNext()) throw new NoSuchElementException();
T value = current.value;
current = current.next;
return value;
}
}
@Override
public Iterator<T> iterator() {
return new LinkedListIterator();
}
}addFirst et LinkedList complète
Ajoutez la possibilité d’insérer des nœuds en tête et observez la classe complète fonctionnelle.
class LinkedList<T> implements Iterable<T> {
private Node<T> head;
private int size;
public void addFirst(T value) {
Node<T> node = new Node<>(value);
node.next = head;
head = node;
size++;
}
public void addLast(T value) {
Node<T> node = new Node<>(value);
if (head == null) { head = node; }
else {
Node<T> curr = head;
while (curr.next != null) curr = curr.next;
curr.next = node;
}
size++;
}
public int size() { return size; }
@Override
public Iterator<T> iterator() {
return new LinkedListIterator();
}
}Utiliser l’Iterator personnalisé
Une fois l’interface Iterable implémentée, la liste chaînée fonctionne dans les boucles pour chaque élément et avec forEach.
LinkedList<String> list = new LinkedList<>();
list.addLast("Alice");
list.addLast("Bob");
list.addLast("Charlie");
// For-each loop works!
for (String name : list) {
System.out.println(name);
}
// Alice
// Bob
// Charlie
// Stream also works (Java 8+)
list.forEach(name -> System.out.println("Hello, " + name));Iterator de plage
Un exemple plus simple : un iterator parcourant une plage numérique sans structure de données sous-jacente.
class IntRange implements Iterable<Integer> {
private final int start, end, step;
IntRange(int start, int end, int step) {
this.start = start; this.end = end; this.step = step;
}
IntRange(int start, int end) { this(start, end, 1); }
@Override
public Iterator<Integer> iterator() {
return new Iterator<>() {
int current = start;
public boolean hasNext() { return current < end; }
public Integer next() {
if (!hasNext()) throw new NoSuchElementException();
int val = current;
current += step;
return val;
}
};
}
}
for (int n : new IntRange(0, 10, 2)) System.out.print(n + " ");
// 0 2 4 6 8Iterator en ordre infixe d’un arbre
Implémentation d’un iterator BST en ordre infixe à l’aide d’une pile explicite : cet exemple montre comment les iterators peuvent remplacer un parcours récursif.
import java.util.*;
class BinaryTree<T extends Comparable<T>> {
private record TreeNode<T>(T val, TreeNode<T> left, TreeNode<T> right) {}
private TreeNode<T> root;
public Iterator<T> inorderIterator() {
Deque<TreeNode<T>> stack = new ArrayDeque<>();
pushLeft(root, stack);
return new Iterator<>() {
public boolean hasNext() { return !stack.isEmpty(); }
public T next() {
TreeNode<T> node = stack.pop();
pushLeft(node.right(), stack);
return node.val();
}
};
}
private void pushLeft(TreeNode<T> node, Deque<TreeNode<T>> stack) {
while (node != null) { stack.push(node); node = node.left(); }
}
}Iterator différé
Les iterators peuvent générer des valeurs de manière différée, uniquement lorsque next() est appelé. C’est utile pour les séquences infinies.
class FibonacciIterator implements Iterator<Long> {
private long a = 0, b = 1;
@Override public boolean hasNext() { return true; } // infinite!
@Override public Long next() {
long result = a;
long next = a + b;
a = b;
b = next;
return result;
}
}
Iterator<Long> fib = new FibonacciIterator();
for (int i = 0; i < 10; i++) System.out.print(fib.next() + " ");
// 0 1 1 2 3 5 8 13 21 34Iterator filtrant
Un iterator décorateur qui encapsule un autre iterator et ignore les éléments qui ne correspondent pas à un prédicat.
import java.util.*;
import java.util.function.*;
class FilterIterator<T> implements Iterator<T> {
private final Iterator<T> source;
private final Predicate<T> predicate;
private T next;
private boolean hasNext;
FilterIterator(Iterator<T> source, Predicate<T> predicate) {
this.source = source; this.predicate = predicate;
advance();
}
private void advance() {
hasNext = false;
while (source.hasNext()) {
T candidate = source.next();
if (predicate.test(candidate)) { next = candidate; hasNext = true; break; }
}
}
public boolean hasNext() { return hasNext; }
public T next() { T val = next; advance(); return val; }
}
List<Integer> nums = List.of(1,2,3,4,5,6,7,8,9,10);
Iterator<Integer> evens = new FilterIterator<>(nums.iterator(), n -> n % 2 == 0);
while (evens.hasNext()) System.out.print(evens.next() + " ");
// 2 4 6 8 10Intégration d’Iterator et des flux
Les iterators personnalisés peuvent être adaptés aux flux à l’aide de Spliterators.spliteratorUnknownSize().
import java.util.*;
import java.util.stream.*;
Iterator<Integer> rangeIt = new IntRange(1, 6).iterator();
Stream<Integer> stream = StreamSupport.stream(
Spliterators.spliteratorUnknownSize(rangeIt, Spliterator.ORDERED),
false // not parallel
);
int sum = stream.mapToInt(Integer::intValue).sum();
System.out.println(sum); // 15Supprimer pendant le parcours
La méthode facultative remove() d’Iterator supprime l’élément renvoyé par le dernier appel à next() ; elle doit être implémentée explicitement dans les iterators personnalisés.
class MutableLinkedList<T> implements Iterable<T> {
// ... (full implementation)
// Iterator with remove support
private class RemovableIterator implements Iterator<T> {
private Node<T> prev = null;
private Node<T> current = head;
public boolean hasNext() { return current != null; }
public T next() {
prev = (prev == null) ? null : current;
T val = current.value;
current = current.next;
return val;
}
public void remove() {
// Remove the last returned node
if (prev == null) head = current;
else prev.next = current;
size--;
}
}
}Liste de vérification pour Iterator
Lors de l’implémentation d’un Iterator personnalisé :
- Appelez toujours
hasNext()avantnext() - Déclenchez
NoSuchElementExceptiondepuisnext()lorsque l’iterator est vide, au lieu de renvoyer null - Rendez l’iterator indépendant de l’état de la collection (ne mettez pas en cache la taille de la collection)
- Utilisez modCount pour détecter les modifications concurrentes si nécessaire
Vérification rapide
Que doit déclencher next() lorsqu’il ne reste plus aucun élément ?
Récapitulatif : implémenter un Iterator personnalisé
Points essentiels :
- Implémentez Iterator<T> avec hasNext(), next() et remove(), qui est facultative
- Conservez dans l’iterator un champ de curseur pointant vers l’élément suivant
- Déclenchez NoSuchElementException depuis next() lorsque hasNext() vaut false
- Créez une nouvelle instance d’iterator à chaque appel à iterator() afin d’obtenir des curseurs indépendants
- Les iterators différés génèrent les valeurs à la demande, ce qui est utile pour les séquences infinies
- Entourez les iterators avec StreamSupport.stream() pour les relier à l’API des flux
Questions Fréquemment Posées
La leçon « Implémenter un itérateur personnalisé » est-elle gratuite ?
Oui — le texte complet de « Implémenter un itérateur personnalisé » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Java Academy, passe à CoddyKit PRO. Le cours Java Academy comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Implémenter un itérateur personnalisé » ?
Créez une classe d’itérateur personnalisée pour une liste chaînée simple ou une structure représentant un intervalle. Tu pratiques Java Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer Java Academy ?
Aucune expérience préalable n'est requise. Java Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 2 sur 4.
Combien de temps prend la leçon « Implémenter un itérateur personnalisé » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon Java Academy ?
Oui. Chaque leçon Java Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Contrats d’Iterable et d’Iterator
- Implémenter un itérateur personnalisé
- ListIterator et parcours bidirectionnel
- Itérateurs à échec rapide et tolérants aux modifications