JavaRush /בלוג Java /Random-HE /דפוס איטרטור

דפוס איטרטור

פורסם בקבוצה
היום נדבר על מה זה Iterator בג'אווה ולמה הוא נחוץ.
תבנית איטרטור - 1
כפי שאתם בטח כבר יודעים, ל-Java יש ממשק Collection נפלא שמיישם את ממשק Iterator. הרשו לי לבצע הזמנה מיד: אין לבלבל את ממשק האיטרטור עם דפוס האיטרטור בג'אווה! וכדי להבהיר, בואו נסתכל תחילה על הממשק.
פשוטו כמשמעו, "איטרטור" יכול להיות מתורגם כ"כוח גס . " כלומר, מדובר בישות מסוימת שיכולה לעבור דרך כל האלמנטים באוסף. יתר על כן, זה מאפשר לך לעשות זאת מבלי להתעמק במבנה הפנימי ובסידור האוספים.
בואו נדמיין לשנייה שאין איטרטור בג'אווה. במקרה זה , כל אחד ואחד יצטרך לצלול לעומקם של הקולקציות ולהבין באמת מה שונה ArrayListמ- LinkedListומ . HashSetTreeSet

שיטות שאיטרטור חייב ליישם

boolean hasNext()- אם עדיין נותרו ערכים באובייקט הניתן לחזרה (כרגע אוסף), השיטה תחזור true, אם אין יותר ערכים false. E next()- מחזירה את הרכיב הבא של האוסף (אובייקט). אם אין יותר אלמנטים (לא היה סימון hasNext(), והתקשרנו next()כשהגענו לסוף האוסף), השיטה תזרוק NoSuchElementException. void remove()- יסיר את האלמנט שהושג לאחרונה על ידי ה- next(). השיטה יכולה לזרוק:
  • UnsupportedOperationException, אם איטרטור זה אינו תומך בשיטה remove()(במקרה של אוספים לקריאה בלבד, למשל)
  • IllegalStateException, אם השיטה next()עדיין לא נקראה, או אם היא remove()כבר נקראה מאז הקריאה האחרונה next().
אז, איטרטור עבור List הוא היישום הנפוץ ביותר. האיטרטור עובר מתחילת האוסף עד סופו: הוא מחפש לראות אם האלמנט הבא קיים ומחזיר אותו אם יש כזה. מחזור נבנה על בסיס האלגוריתם הפשוט הזה for-each. ההרחבה שלו היא ListIterator. בואו נסתכל על שיטות איטרטור לרשימת ג'אווה נוספות. סביר להניח שאתה מכיר אותם:
  • 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, כפי שאתה ודאי יודע (ואם אתה לא יודע, תבין את זה), מרחיב את הממשק Iterable, אבל זה לא אומר שרק List, Setותומכים Queueבאיטרטור. For java Map iteratorנתמך גם כן, אך יש לקרוא ל Map.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());
}
אתה חושב: "תפסיק. אנחנו מדברים על הממשק, וכותרת המאמר אומרת "דפוס". כלומר, תבנית האיטרטור היא ממשק האיטרטור? או שהממשק הוא דפוס? אם המילה הזו מופיעה בפעם הראשונה, אני נותן לך התייחסות: דפוס הוא דפוס עיצובי, התנהגות מסוימת שכיתה או מחלקות רבות הקשורות זו לזו חייבות לדבוק בה. ניתן ליישם איטרטור ב-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);
	}}
עכשיו בואו נבצע ניתוח מורפמי של המילה "מהר":
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. לְנַחֵם
  4. שורש
בעת עיצוב איטרטור משלך, אתה יכול לציין את אלגוריתם האיטרציה כרצונך. בהצלחה בלימודים!
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION