JavaRush /Blog Java /Random-FR /Modèle d'itérateur

Modèle d'itérateur

Publié dans le groupe Random-FR
Aujourd'hui, nous allons parler de ce qu'est Iterator en Java et pourquoi il est nécessaire.
Modèle d'itérateur - 1
Comme vous le savez probablement déjà, Java possède une merveilleuse interface Collection qui implémente l'interface Iterator. Permettez-moi de faire une réserve tout de suite : l'interface itérateur ne doit pas être confondue avec le modèle itérateur en Java ! Et pour clarifier, regardons d’abord l’interface.
Littéralement, « Iterator » peut être traduit par « force brute » . Autrement dit, il s’agit d’une certaine entité qui peut parcourir tous les éléments de la collection. De plus, cela vous permet de le faire sans vous plonger dans la structure interne et la disposition des collections.
Imaginons une seconde qu'il n'y ait pas d'itérateur en Java. Dans ce cas, chacun devra plonger au plus profond des collections et bien comprendre ce qui est différent ArrayListde LinkedListet HashSetde TreeSet.

Méthodes que l'itérateur doit implémenter

boolean hasNext()— s'il reste encore des valeurs dans l'objet itérable (actuellement une collection), la méthode renverra true, s'il n'y a plus de valeurs false. E next()— renvoie l'élément suivant de la collection (objet). S'il n'y a plus d'éléments (il n'y a pas eu de check hasNext()et nous avons appelé next()lorsque nous avons atteint la fin de la collection), la méthode lancera NoSuchElementException. void remove()- supprimera le dernier élément obtenu par le next(). La méthode peut lancer :
  • UnsupportedOperationException, si cet itérateur ne supporte pas la méthode remove()(dans le cas de collections en lecture seule par exemple)
  • IllegalStateException, si la méthode next()n'a pas encore été appelée, ou si elle remove()a déjà été appelée depuis le dernier appel next().
Ainsi, l’itérateur pour List est l’implémentation la plus courante. L'itérateur va du début à la fin de la collection : il regarde si l'élément suivant est présent et le renvoie s'il y en a un. Un cycle est construit sur la base de cet algorithme simple for-each. Son extension est ListIterator. Examinons d'autres méthodes d'itérateur de liste Java. Vous les connaissez probablement :
  • void add(E e)— insère un élément Edans ;List
  • boolean hasPrevious()— reviendra trues'il Listy a des éléments lors de la recherche inversée ;
  • int nextIndex()— renverra l'index de l'élément suivant ;
  • E previous()— renverra l'élément de feuille précédent ;
  • int previousIndex()— renverra l'index de l'élément précédent ;
  • void set(E e)- remplacera l'élément renvoyé par le dernier appel next()ou previous()par l'élément e.
Regardons un petit exemple. Créons un Listcontenant les lignes de salutations aux étudiants :
List<String> list = new ArrayList<>();
list.add("Hello");
list.add("Обучающимся");
list.add("На");
list.add("JavaRush");
Nous allons maintenant obtenir un itérateur pour cela et imprimer toutes les lignes contenues sur la console :
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}
Il y aura maintenant un « goulot d'étranglement » : les collections Java, comme vous le savez probablement (et si vous ne le savez pas, comprenez-le), étendent l'interface Iterable, mais cela ne signifie pas seulement List, Setet Queueprennent en charge un itérateur. For java Map iteratorest également supporté, mais doit être appelé pourMap.entrySet() :
Map<String, Integer> map = new HashMap<>();
Iterator mapIterator = map.entrySet().iterator();
Ensuite, la méthode next() renverra un objet Entrycontenant une paire « clé »-« valeur ». Ensuite tout est pareil avec List:
while (mapIterator.hasNext()) {
    Map.Entry<String, Integer> entry = mapIterator.next();
    System.out.println("Key: " + entry.getKey());
    System.out.println("Value: " + entry.getValue());
}
Vous pensez : « Arrêtez. Nous parlons de l'interface, et le titre de l'article dit « Pattern ». Autrement dit, le modèle d'itérateur est l'interface Iterator ? Ou l'interface est-elle un modèle ? Si ce mot apparaît pour la première fois, je vous donne une référence : un pattern est un design pattern, un certain comportement auquel une classe ou plusieurs classes interconnectées doivent adhérer. Un itérateur en Java peut être implémenté pour tout objet dont la structure interne implique une itération, et vous pouvez modifier la signature des méthodes discutées. L'essentiel lors de la mise en œuvre d'un modèle est la logique à laquelle la classe doit adhérer. L'interface itérateur est une implémentation privée du modèle du même nom, appliqué à la fois aux structures prêtes à l'emploi ( List, Set, Queue, Map) et à d'autres, à la discrétion du programmeur. En étendant l'interface Iterator, vous implémentez un modèle, mais vous n'avez pas besoin d'étendre l'interface pour implémenter le modèle. Une analogie simple : tous les poissons nagent, mais tout ce qui nage n’est pas un poisson. A titre d'exemple, j'ai décidé de prendre... le mot. Plus précisément, un nom. Il se compose de parties : préfixe, racine, suffixe et terminaison. Pour des parties d'un mot, nous allons créer une interface WordPartet des classes qui l'étendent :Prefix, Root, Suffix и Ending :
interface WordPart {
    String getWordPart();
}

static class Root implements WordPart {

    private String part;

    public Root(String part) {
        this.part = part;
    }

    @Override
    public String getWordPart() {
        return part;
    }
}

static class Prefix implements WordPart {

    private String part;

    public Prefix(String part) {
        this.part = part;
    }

    @Override
    public String getWordPart() {
        return part;
    }
}

static class Suffix implements WordPart {

    private String part;

    public Suffix(String part) {
        this.part = part;
    }

    @Override
    public String getWordPart() {
        return part;
    }
}

static class Ending implements WordPart {

    private String part;

    public Ending(String part) {
        this.part = part;
    }

    @Override
    public String getWordPart() {
        return part;
    }
}
Ensuite, la classe Word(le mot) contiendra des parties, et en plus d'elles nous ajouterons un entier reflétant le nombre de parties dans le mot :
public class Word {

    private Root root;
    private Prefix prefix;
    private Suffix suffix;
    private Ending ending;
    private int partCount;

    public Word(Root root, Prefix prefix, Suffix suffix, Ending ending) {
        this.root = root;
        this.prefix = prefix;
        this.suffix = suffix;
        this.ending = ending;
        this.partCount = 4;
    }

    public Word(Root root, Prefix prefix, Suffix suffix) {
        this.root = root;
        this.prefix = prefix;
        this.suffix = suffix;
        this.partCount = 3;

    }

    public Word(Root root, Prefix prefix) {
        this.root = root;
        this.prefix = prefix;
        this.partCount = 2;
    }

    public Word(Root root) {
        this.root = root;
        this.partCount = 1;
    }

    public Root getRoot() {
        return root;
    }

    public Prefix getPrefix() {
        return prefix;
    }

    public Suffix getSuffix() {
        return suffix;
    }

    public Ending getEnding() {
        return ending;
    }

    public int getPartCount() {
        return partCount;
    }

    public boolean hasRoot() {
        return this.root != null;
    }

    public boolean hasPrefix() {
        return this.prefix != null;
    }

    public boolean hasSuffix() {
        return this.suffix != null;
    }

    public boolean hasEnding() {
        return this.ending != null;
    }
D'accord, nous avons quatre constructeurs surchargés (pour plus de simplicité, supposons que nous ne pouvons avoir qu'un seul suffixe). Un nom ne peut pas être constitué d'un seul préfixe, donc pour un constructeur avec un paramètre, nous définirons la racine. Écrivons maintenant une implémentation du modèle d'itérateur : WordIterator, en remplaçant 2 méthodes : hasNext()etnext() :
public class WordIterator implements Iterator<Word.WordPart> {

    private Word word;
    private int wordPartsCount;

    public WordIterator(Word word) {
        this.word = word;
        this.wordPartsCount = word.getPartCount();
    }

    @Override
    public boolean hasNext() {
        if (wordPartsCount == 4) {
            return word.hasPrefix() || word.hasRoot() || word.hasSuffix() || word.hasEnding();
        } else if (wordPartsCount == 3) {
            return word.hasPrefix() || word.hasRoot() || word.hasSuffix();
        } else if (wordPartsCount == 2) {
            return word.hasPrefix() || word.hasRoot();
        } else if (wordPartsCount == 1) {
            return word.hasRoot();
        }
        return false;
    }

    @Override
    public Word.WordPart next() throws NoSuchElementException {
        if (wordPartsCount <= 0) {
            throw new NoSuchElementException("No more elements in this word!");
        }

        try {
            if (wordPartsCount == 4) {
                return word.getEnding();
            }
            if (wordPartsCount == 3) {
                return word.getSuffix();
            }
            if (wordPartsCount == 2) {
                return word.getPrefix();
            }
            return word.getRoot();
        } finally {
            wordPartsCount--;
        }
    }
}
Il ne reste plus qu'à affecter l'itérateur à la classe Word:
public class Word implements Iterable<Word.WordPart> {@Override
	public Iterator<WordPart>iterator() {
    		return new WordIterator(this);
	}}
Procédons maintenant à une analyse morphémique du mot « rush » :
public class Main {
    public static void main(String[] args) {
        Word.Root root = new Word.Root("беж");
        Word.Prefix prefix = new Word.Prefix("пере");
        Word.Suffix suffix = new Word.Suffix("к");
        Word.Ending ending = new Word.Ending("a");

        Word word = new Word(root, prefix, suffix, ending);

        Iterator wordIterator = word.iterator();
        while (wordIterator.hasNext()) {
            Word.WordPart part = (Word.WordPart) wordIterator.next();
            System.out.println(part.getClass() + ": " + part.getWordPart());
        }
    }
}
Veuillez noter que dans mon implémentation du modèle d'itérateur, j'ai choisi l'ordre de sortie suivant :
  1. fin
  2. suffixe
  3. console
  4. racine
Lors de la conception de votre propre itérateur, vous pouvez spécifier l'algorithme d'itération à votre guise. Bonne chance dans tes études!
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION