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 ?