Série 8 – Itérateurs
Introduction
Le but de cette série est d'examiner l'utilisation des itérateurs, donc du patron Iterator, dans un contexte différent de celui des collections. En particulier, il vous sera demandé de définir des itérateurs infinis, c-à-d toujours capables de produire une prochaine valeur.
Exercice 1 – itérateur en lecture seule
Définissez une classe abstraite générique nommée ReadOnlyIterator
qui implémente l'interface Iterator
de java.util
et qui n'implémente rien d'autre que la méthode remove
de cette interface. Cette méthode, finale, se contente de lever l'exception UnsupportedOperationException
.
Cette classe servira de classe-mère à tous les itérateurs qui ne gèrent pas la suppression d'éléments.
Exercice 2 – itérateur infini
Définissez une sous-classe abstraite de ReadOnlyIterator
, générique elle aussi et nommée InfiniteIterator
, dont la méthode hasNext
retourne toujours vrai.
Cette classe servira de classe-mère à tous les itérateurs infinis, qui ont toujours un élément suivant à fournir.
Exercice 3 – itérateur entier infini
Définissez une sous-classe concrète de InfiniteIterator
nommée IntIterator
et modélisant un itérateur sur les entiers. Cet itérateur est paramétrisé par un entier de départ \(f\) (pour from) et un pas \(s\) (pour step), et fournit successivement les entiers \(f\), \(f+s\), \(f+2s\), \(f+3s\), etc.
Par exemple, en construisant un tel itérateur en lui passant 0 comme entier de départ et 1 comme pas, on obtient un itérateur sur les nombres naturels. En passant 2 comme entier de départ et 2 comme pas, on obtient un itérateur sur les nombres pairs strictement positifs. Et ainsi de suite…
Une fois la classe écrite, assurez-vous qu'elle fonctionne en écrivant un petit programme de test qui affiche quelques valeurs d'un tel itérateur.
Exercice 4 – itérateur filtrant
Définissez une sous-classe abstraite de ReadOnlyIterator
nommée FilteringIterator
, générique, dont le but est de filtrer les valeurs retournées par un autre itérateur, appelé l'itérateur sous-jacent.
L'itérateur sous-jacent est passé au constructeur de la classe FilteringIterator
et cette dernière est équipée d'une méthode abstraite ayant le profil suivant :
public abstract boolean shouldInclude(E elem);
C'est cette méthode qui détermine quels éléments de l'itérateur sous-jacent doivent être retournés par l'itérateur filtré, qui sont ceux pour lesquels elle retourne vrai.
Par exemple, pour écrire un itérateur filtrant qui n'inclut que les entiers impairs fournis par un itérateur d'entiers sous-jacent, on peut définir une sous-classe de FilteringIterator
ainsi :
public final class OddFilteringIterator extends FilteringIterator<Integer> { public OddFilteringIterator(Iterator<Integer> underlyingIt) { super(underlyingIt); } @Override public boolean shouldInclude(Integer elem) { return (elem % 2) != 0; } }
Une fois la classe écrite, assurez-vous également qu'elle fonctionne en modifiant votre programme de test pour qu'il filtre, p.ex. comme illustré ci-dessus, les éléments fournis par un itérateur de votre choix.
Exercice 5 – itérateur filtrant les multiples
Définissez finalement une sous-classe concrète de FilteringIterator
nommée MultiplesFilteringIterator
et ayant la particularité de supprimer de l'itérateur d'entiers sous-jacent tous les multiples des entiers que celui-ci a fourni jusqu'à présent.
Par exemple, lorsqu'on l'applique à l'itérateur fournissant tous les entiers à partir de 2 (à savoir 2, 3, 4, 5, 6, 7, …), cet itérateur filtrant doit fournir 2, puis 3, mais pas 4 car il s'agit d'un multiple de 2, déjà fourni précédemment. Ensuite, il doit fournir 5 mais pas 6 car il s'agit d'un multiple de 2 et de 3, déjà fournis précédemment. Et ainsi de suite.
Attention, cela implique de définir non seulement la méthode shouldInclude
mais également de redéfinir la méthode next
afin de mémoriser les différents entiers produits ! Ces entiers peuvent être mémorisés dans une collection de votre choix, p.ex. une liste ou un ensemble.
Une fois votre classe terminée, écrivez un petit programme appliquant votre itérateur filtrant les multiples à l'itérateur d'entiers produisant tous les entiers à partir de 2 et affichez les 20 premières valeurs qu'il produit. Quelle est la caractéristique de ces valeurs ?