JavaRush /Java Blog /Random-IT /Modello iteratore

Modello iteratore

Pubblicato nel gruppo Random-IT
Oggi parleremo di cos'è Iterator in Java e perché è necessario.
Modello iteratore - 1
Come probabilmente già saprai, Java ha una meravigliosa interfaccia Collection che implementa l'interfaccia Iterator. Faccio subito una prenotazione: l'interfaccia iteratore non deve essere confusa con il pattern iteratore in Java! E per chiarire, diamo prima un'occhiata all'interfaccia.
Letteralmente “Iteratore” può essere tradotto come “forza bruta ” . Cioè, è una certa entità che può scorrere tutti gli elementi della raccolta. Inoltre, ti consente di farlo senza approfondire la struttura interna e la disposizione delle collezioni.
Immaginiamo per un secondo che non ci sia un iteratore in Java. In questo caso, ognuno dovrà tuffarsi nel profondo delle collezioni e capire veramente cosa è diverso ArrayListda LinkedListe HashSetda TreeSet.

Metodi che Iterator deve implementare

boolean hasNext()— se sono rimasti ancora valori nell'oggetto iterabile (attualmente una Collection), il metodo restituirà true, se non ci sono più valori false. E next()— restituisce l'elemento successivo della raccolta (oggetto). Se non ci sono più elementi (non c'era alcun check hasNext()e abbiamo chiamato next()quando abbiamo raggiunto la fine della raccolta), il metodo lancerà NoSuchElementException. void remove()- rimuoverà l'elemento ottenuto per ultimo dal next(). Il metodo può lanciare:
  • UnsupportedOperationException, se questo iteratore non supporta il metodo remove()(nel caso di raccolte di sola lettura, ad esempio)
  • IllegalStateException, se il metodo next()non è stato ancora chiamato oppure se remove()è già stato chiamato dall'ultima chiamata next().
Pertanto, l'iteratore per List è l'implementazione più comune. L'iteratore va dall'inizio alla fine della raccolta: controlla se è presente l'elemento successivo e lo restituisce se ce n'è uno. Un ciclo viene costruito sulla base di questo semplice algoritmo for-each. La sua estensione è ListIterator. Diamo un'occhiata ad ulteriori metodi iteratori dell'elenco Java. Molto probabilmente li conosci:
  • void add(E e)— inserisce un elemento Ein ;List
  • boolean hasPrevious()— ritornerà truese Listci sono elementi durante la ricerca inversa;
  • int nextIndex()— restituirà l'indice dell'elemento successivo;
  • E previous()— restituirà l'elemento del foglio precedente;
  • int previousIndex()— restituirà l'indice dell'elemento precedente;
  • void set(E e)- sostituirà l'elemento restituito dall'ultima chiamata next()o previous()con l'elemento e.
Diamo un'occhiata a un piccolo esempio. Creiamo un file Listcontenente le righe di saluto agli studenti:
List<String> list = new ArrayList<>();
list.add("Hello");
list.add("Обучающимся");
list.add("На");
list.add("JavaRush");
Ora otterremo un iteratore e stamperemo tutte le righe contenute sulla console:
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}
Adesso ci sarà un “collo di bottiglia”: Java Collections, come probabilmente saprai (e se non lo sai, capiscilo), estendono l’interfaccia Iterable, ma questo non significa solo List, Sete Queuesupportano l’iteratore. java Map iteratorAnche For è supportato, ma deve essere richiamato per Map.entrySet():
Map<String, Integer> map = new HashMap<>();
Iterator mapIterator = map.entrySet().iterator();
Quindi il metodo next() restituirà un oggetto Entrycontenente una coppia "chiave" - ​​"valore". Quindi tutto è uguale con List:
while (mapIterator.hasNext()) {
    Map.Entry<String, Integer> entry = mapIterator.next();
    System.out.println("Key: " + entry.getKey());
    System.out.println("Value: " + entry.getValue());
}
Pensi: “Smettila. Stiamo parlando dell'interfaccia e il titolo dell'articolo dice “Pattern”. Cioè, il modello iteratore è l'interfaccia Iterator? Oppure l'interfaccia è uno schema? Se questa parola appare per la prima volta ti do un riferimento: un pattern è un design pattern, un determinato comportamento a cui una classe o più classi interconnesse devono attenersi. Un iteratore in Java può essere implementato per qualsiasi oggetto la cui struttura interna implica l'iterazione ed è possibile modificare la firma dei metodi discussi. La cosa principale quando si implementa un modello è la logica a cui la classe deve aderire. L'interfaccia iteratore è un'implementazione privata del pattern omonimo, applicata sia a strutture già pronte ( List, Set, Queue, Map), sia ad altre, a discrezione del programmatore. Estendendo l'interfaccia Iterator, implementi un modello, ma non è necessario estendere l'interfaccia per implementare il modello. Una semplice analogia: tutti i pesci nuotano, ma non tutto ciò che nuota è un pesce. Ad esempio, ho deciso di prendere... la parola. Più specificamente, un sostantivo. È composto da parti: prefisso, radice, suffisso e desinenza. Per parti di una parola, creeremo un'interfaccia WordParte classi che la estendono: 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;
    }
}
Quindi la classe Word(parola) conterrà parti e oltre a queste aggiungeremo un numero intero che riflette il numero di parti nella parola:
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;
    }
Ok, abbiamo quattro costruttori sovraccaricati (per semplicità, supponiamo di poter avere un solo suffisso). Un sostantivo non può essere costituito da un prefisso, quindi per un costruttore con un parametro imposteremo la radice. Ora scriviamo un'implementazione del pattern iteratore: WordIterator, sovrascrivendo 2 metodi: hasNext()e next():
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--;
        }
    }
}
Non resta che assegnare l'iteratore alla classe Word:
public class Word implements Iterable<Word.WordPart> {@Override
	public Iterator<WordPart>iterator() {
    		return new WordIterator(this);
	}}
Ora conduciamo un’analisi morfemica della parola “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());
        }
    }
}
Tieni presente che nella mia implementazione del modello iteratore, ho scelto il seguente ordine di output:
  1. fine
  2. suffisso
  3. consolle
  4. radice
Quando progetti il ​​tuo iteratore, puoi specificare l'algoritmo di iterazione come desideri. Buona fortuna per i tuoi studi.
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION