JavaRush /Java Blog /Random-TW /迭代器模式

迭代器模式

在 Random-TW 群組發布
今天我們將討論Java 中的 Iterator 是什麼以及為什麼需要它。
迭代器模式 - 1
您可能已經知道,Java 有一個出色的 Collection 接口,它實作了 Iterator 接口。讓我立即保留意見:迭代器介面不應該與Java中的迭代器模式混淆!為了澄清這一點,我們先來看看介面。
從字面上看,“迭代器”可以翻譯為“蠻力” 。也就是說,它是某個實體,可以迭代集合中的所有元素。此外,它允許您無需深入研究集合的內部結構和排列即可做到這一點。
讓我們想像一下 Java 中沒有迭代器。在這種情況下,每個人都必須深入集合的最深處,真正理解什麼是 ArrayListLinkedList什麼HashSet不同TreeSet

Iterator必須實作的方法

boolean hasNext()— 如果可迭代物件(目前是 Collection)中仍然有值,則該方法將傳回true(如果沒有更多值)falseE next()— 傳回集合(物件)的下一個元素。如果沒有更多元素(沒有 check ,我們在到達集合末尾時hasNext()調用),則該方法將拋出。 - 將刪除最後獲得的元素。該方法可以拋出: next()NoSuchElementExceptionvoid remove()next()
  • UnsupportedOperationException,如果此迭代器不支援該方法remove()(例如,在只讀集合的情況下)
  • IllegalStateException,如果該方法next()尚未被調用,或remove()自上次調用以來已被調用next()
因此,List 的迭代器是最常見的實作。迭代器從集合的開頭到結尾:它會查看是否存在下一個元素,如果存在則傳回它。一個循環就是建立在這個簡單演算法的基礎上的for-each。它的擴充是ListIterator。讓我們看看其他的 java 列表迭代器方法。您很可能認識他們:
  • void add(E e)— 插入一個元素E到;List
  • boolean hasPrevious()true— 反向查找時如果List有元素則回傳;
  • int nextIndex()— 將會傳回下一個元素的索引;
  • E previous()— 將傳回前一個工作表元素;
  • int previousIndex()— 將會傳回前一個元素的索引;
  • void set(E e)- 將取代上次呼叫傳回的元素next()previous()的元素e
讓我們來看一個小例子。讓我們建立一個List包含對學生的問候語的行:
List<String> list = new ArrayList<>();
list.add("Hello");
list.add("Обучающимся");
list.add("На");
list.add("JavaRush");
現在我們將獲得它的迭代器並將所有包含的行列印到控制台:
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}
現在就會出現一個「瓶頸」:Java Collections,正如您可能知道的那樣(如果您不知道,請弄清楚),擴展接口,Iterable但這並不意味著僅支援迭代器。For也受支持,但必須呼叫 for : ListSetQueuejava Map iteratorMap.entrySet()
Map<String, Integer> map = new HashMap<>();
Iterator mapIterator = map.entrySet().iterator();
然後該方法next() 將傳回一個Entry包含“鍵”-“值”對的物件。那麼一切都與以下相同List
while (mapIterator.hasNext()) {
    Map.Entry<String, Integer> entry = mapIterator.next();
    System.out.println("Key: " + entry.getKey());
    System.out.println("Value: " + entry.getValue());
}
你想:「停下來。我們正在談論介面,文章的標題說「模式」。也就是說,迭代器模式就是Iterator介面?或者介面是一種模式嗎?如果這個字第一次出現,我給你一個參考:模式是一種設計模式,是一個類別或許多互連的類別必須遵守的某種行為。java中的迭代器可以為任何內部結構涉及迭代的物件實現,並且您可以更改正在討論的方法的簽章。實現模式時最重要的是類別必須遵守的邏輯。 迭代器介面是同名模式的私有實現,List, Set, Queue, Map由程式設計師自行決定應用於現成的結構 ( ) 和其他結構。透過擴展 Iterator 接口,您可以實現一個模式,但不需要擴展該接口來實現該模式。一個簡單的比喻:所有的魚都會游泳,但不是所有會游泳的都是魚。作為一個例子,我決定採用……這個字。更具體地說,是一個名詞。它由幾個部分組成:前綴、詞根、後綴和詞尾。對於單字的一部分,我們將建立一個介面WordPart和擴展它的類別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;
    }
}
然後類別Word(單字)將包含部分,除了它們之外,我們還將添加一個反映單字中部分數量的整數:
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;
    }
好的,我們有四個重載的建構子(為簡單起見,我們假設我們只能有一個後綴)。名詞不能由一個前綴組成,因此對於具有一個參數的建構函數,我們將設定根。現在讓我們來寫一個迭代器模式的實作:WordIterator,重寫 2 個方法:hasNext()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--;
        }
    }
}
剩下的就是將迭代器分配給類別Word
public class Word implements Iterable<Word.WordPart> {@Override
	public Iterator<WordPart>iterator() {
    		return new WordIterator(this);
	}}
現在我們對「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());
        }
    }
}
請注意,在迭代器模式的實作中,我選擇了以下輸出順序:
  1. 結尾
  2. 後綴
  3. 安慰
在設計自己的迭代器時,您可以根據需要指定迭代演算法。祝你學習順利!
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION