Сьогодні поговоримо про те, що таке Iterator у Java і навіщо він потрібний.
Як ти вже, мабуть, знаєш, Java має чудовий інтерфейс Collection, що реалізує інтерфейс Iterator. Відразу обмовлюся, не слід плутати інтерфейс iterator з патерном iterator у Java! І щоб внести ясність, спочатку розберемося саме з інтерфейсом.
Уявимо на секунду, що iterator у Java відсутня. У такому разі всім і кожному доведеться пірнути в самі глибини колекцій і по-справжньому розібратися, чим відрізняється
| Дослівно "Iterator" можна перекласти як "перебірник" . Тобто, це якась сутність, здатна перебрати всі елементи в колекції. При цьому вона дозволяє це зробити без вникання у внутрішню структуру та влаштування колекцій. |
ArrayListвід LinkedListі HashSetвід TreeSet.
Методи, які має імплементувати Iterator
boolean hasNext()— якщо в об'єкті, що ітерується (поки що це Collection) залишабося ще значення — метод поверне true, якщо значення скінчабося false. E next()- Повертає наступний елемент колекції (об'єкта). Якщо елементів більше немає (не було перевірки hasNext(), а ми викликали next(), досягнувши кінця колекції), метод кине NoSuchElementException. void remove()- Видалить елемент, який був востаннє отриманий методом next(). Метод може залишити:
UnsupportedOperationExceptionякщо цей ітератор не підтримує методremove()(у випадку з read-only колекціями, наприклад)IllegalStateExceptionякщо методnext()ще не був викликаний, або якщоremove()вже був викликаний після останнього викликуnext().
for-each. Його розширення - ListIterator. Розглянемо додаткові способи java list iterator. Ти їх, швидше за все, знаєш:
void add(E e)- Вставляє елементEв ;Listboolean hasPrevious()- Повернеtrue, якщо при зворотному переборіListє елементи;int nextIndex()- Поверне індекс наступного елемента;E previous()- Поверне попередній елемент листа;int previousIndex()- Поверне індекс попереднього елемента;void set(E e)— замінить елемент, повернутий останнім викликомnext()абоprevious()елементe.
List, що містить у собі рядки привітання учнів:
List<String> list = new ArrayList<>();
list.add("Вітання");
list.add("Обучающимся");
list.add("На");
list.add("JavaRush"); Тепер отримаємо для нього ітератор і виведемо в консоль усі рядки:
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
} Зараз буде "вузьке місце": Java Collections як ти, ймовірно, знаєш (а якщо не знаєш, розберися), розширюють інтерфейс, Iterableале це не означає, що тільки List, Setі Queueпідтримують ітератор. Також підтримується 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());
} Ти думаєш: «Стоп. Ми говоримо про інтерфейс, а в заголовку статті написано "Паттерн". Тобто патерн iterator – це інтерфейс 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);
}
…
} Тепер проведемо морфемний розбір слова «перебіжка»:
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());
}
}
} Зверніть увагу, у своїй реалізації патерну iterator я вибрав наступний порядок виведення:
- закінчення
- суфікс
- префікс
- корінь
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ