JavaRush /Java блогу /Random-KY /Итератор үлгүсү
Алексей Дмитревский
Деңгээл
Москва

Итератор үлгүсү

Группада жарыяланган
Бүгүн биз Java'да Iterator деген эмне жана ал эмне үчүн керек экендиги жөнүндө сүйлөшөбүз .
Итератор үлгүсү - 1
Сиз билгендей, Javaда Iterator интерфейсин ишке ашырган сонун Collection интерфейси бар. Мага дароо ээлеп коюуга уруксат этиңиз: итератор интерфейсин Javaдагы итератор үлгүсү менен чаташтырбоо керек! Ал эми тактоо үчүн, адегенде интерфейсти карап көрөлү.
Сөзмө-сөз, "Итератор" "кара күч " деп которулса болот . Башкача айтканда, бул коллекциядагы бардык элементтерди кайталай турган белгилүү бир нерсе. Мындан тышкары, бул коллекциялардын ички түзүмүн жана тартибин изилдебестен жасоого мүмкүндүк берет.
Келгиле, бир секундага Javaда итератор жок экенин элестетип көрөлү. Бул учурда, ар бир адам коллекциялардын тереңине сүңгүп, эмнеси менен айырмаланарын чындап ArrayListтүшүнүшү керек . LinkedListHashSetTreeSet

Итератор ишке ашырууга тийиш болгон ыкмалар

boolean hasNext()— эгерде кайталануучу an objectте дагы деле маанилер болсо (учурда Коллекция), trueэгер башка маанилер жок болсо, метод кайтып келет false. E next()— коллекциянын (an objectтин) кийинки элементин кайтарат. Эгер дагы элементтер жок болсо (чек болгон жок жана биз коллекциянын аягына жеткенде hasNext()чакырдык ), ыкма ыргытат . - акыркы жолу алынган элементти алып салат . Метод ыргыта алат: next()NoSuchElementExceptionvoid remove()next()
  • UnsupportedOperationException, эгерде бул итератор методду колдоого албаса remove()(мисалы, окуу үчүн гана жыйнактарда)
  • IllegalStateException, эгерде метод next()али чакырыла элек болсо же ал remove()акыркы чакыруудан бери чакырылса next().
Ошентип, List үчүн итератор эң кеңири таралган ишке ашыруу болуп саналат. Итератор коллекциянын башынан аягына чейин барат: кийинки элементтин бар-жогу жок экенин карап, эгер бар болсо, аны кайтарат. Бул жөнөкөй алгоритмдин негизинде цикл курулат for-each. Анын кеңейтorши 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 да колдоого алынат, бирок чакырылышы керек : ListSetQueuejava Map iteratorMap.entrySet()
Map<String, Integer> map = new HashMap<>();
Iterator mapIterator = map.entrySet().iterator();
Андан кийин ыкма "ачкыч" - "маани" жупту камтыган next() an objectти кайтарат . 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 тorнде итератор ички түзүмү итерациялоону камтыган ар кандай an object үчүн ишке ашырылышы мүмкүн жана талкууланган ыкмалардын кол тамгасын өзгөртүүгө болот. Үлгү ишке ашырууда негизги нерсе класс карманууга тийиш болгон логика. Итератор интерфейси – бул даяр структураларга ( 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. тамыр
Өзүңүздүн итераторуңузду долбоорлоодо сиз итерация алгоритмин каалагандай көрсөтсөңүз болот. Окууңарга ийгorк!
Комментарийлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION