1. Передісторія появи ітератора
Ви вже познайомилися з множиною HashSet. І якщо ви дійсно розбиралися з ним, а не просто читали лекцію, повинні були задатися питанням:
А як вивести список всіх елементів HashSet на екран? Адже методів get() та set() у множини немає!
І HashSet у своїй проблемі не одинокий. Окрім HashSet, є ще багато різних колекцій, у яких не можна отримати елемент за номером, адже чіткий порядок елементів відсутній.
Програмісти свого часу винайшли багато складних структур даних, таких як граф, дерево. Або, наприклад, список списків.
Багато контейнерів змінюють порядок своїх елементів при додаванні нових або видаленні існуючих елементів. Наприклад, є список, який зберігає елементи у відсортованому порядку, і при додаванні нового елемента він практично завжди вставляється в середину списку.
Ось ми і отримуємо ситуацію, що контейнер, який містить елементи, є, самі елементи теж є, а фіксованого порядку немає.
Але припустимо, ми хочемо скопіювати всі елементи з такої колекції в масив або список. Нам потрібно отримати всі елементи. Нам байдуже, в якому порядку ми їх обійдемо, головне — не повторюватися. Як нам це зробити?
2. Ітератор у колекції
Як рішення вищеописаної проблеми було запропоновано рішення — ітератор.
Ітератор — це спеціальний об'єкт у колекції, який допомагає обійти всі елементи колекції і не повторюватися.
Отримати ітератор у будь-якої колекції можна за допомогою коду:
Iterator<Тип> it = ім'я.iterator();
Де ім'я — це ім'я змінної-колекції, Тип — це тип елементів колекції. iterator() — це метод колекції. it — це ім'я змінної-об'єкта-ітератора.
У об'єкта-ітератора є 3 методи:
| Метод | Опис |
|---|---|
|
Повертає черговий елемент колекції |
|
Перевіряє, чи є ще не пройдені елементи |
|
Видаляє поточний елемент колекції |
Ці методи чимось схожі на методи класу Scanner: nextInt() і hasNextInt().
Метод next() повертає черговий елемент колекції, у якої ми отримали ітератор.
Метод hasNext() перевіряє, чи є ще елементи в колекції, які ітератор не повернув.
Ось як можна вивести на екран всі елементи множини HashSet:
| Код | Примітки |
|---|---|
|
Створюємо об'єкт типу HashSet, який зберігає елементи типу String.Додаємо в set вітання на різних мовах.Отримуємо об'єкт-ітератор у множині set.Поки є ще елементи Отримуємо наступний елемент Виводимо елемент на екран |
3. Цикл for-each
Основний мінус ітератора у тому, що з його використанням код стає ще більш громіздким, ніж з використанням циклу for.
Давайте для порівняння виведемо на екран список за допомогою циклу for і за допомогою ітератора:
| Ітератор | Цикл for |
|---|---|
|
|
Так, елементи списку ArrayList набагато краще обходити за допомогою циклу — все коротше виходить.
Однак розробники Java знову вирішили підсипати нам цукру. І на наше щастя, це знову був синтаксичний цукор.
Вони додали в Java новий вид циклів і назвали його for-each. Ось як виглядає його використання в загальному випадку:
for(Тип ім'я:колекція)
Де колекція — це ім'я змінної колекції, Тип — це тип елементів колекції, а ім'я — це ім'я змінної, яка на кожному витку циклу приймає чергове значення з колекції.
Цей цикл обходить усі елементи колекції за допомогою прихованого ітератора. Ось як він насправді працює:
| Цикл for-each | Що бачить компілятор: Цикл з ітератором |
|---|---|
|
|
Коли компілятор зустріне у вашому коді цикл for-each, він просто замінить його на код справа: додасть метод отримання ітератора і всі необхідні виклики методів.
Програмісти дуже люблять цикл for-each і практично завжди використовують його, коли потрібно обійти всі елементи колекції.
Навіть обхід списку ArrayList за допомогою циклу for-each виглядає коротше:
| Цикл for-each | Цикл for |
|---|---|
|
|
4. Видалення елемента в циклі for-each
У циклу for-each є один мінус: він не вміє правильно видаляти елементи. Якщо ви напишете такий код, отримаєте помилку.
| Код | Примітка |
|---|---|
|
При видаленні виникне помилка! |
Це дуже красивий і зрозумілий код, тільки працювати він не буде.
Не можна змінювати колекцію, поки ви обробляєте її за допомогою ітератора.
Є три способи обійти це обмеження.
1 Використання іншого циклу
Якщо ви обробляєте колекцію ArrayList, можете скористатися звичайним циклом зі лічильником i.
| Код |
|---|
|
Однак цей варіант не підходить для колекцій HashSet і HashMap
2 Явне використання ітератора
Можна використовувати ітератор явно та задіяти його метод remove().
| Робочий варіант | Неробочий варіант |
|---|---|
|
|
Зверніть увагу, що метод remove() ми викликаємо у об'єкта-ітератора! Ітератор «знатиме» про видалення елемента і зможе правильно обробити цю ситуацію.
3 Використання копії колекції
Також ви можете створити копію колекції та використовувати в циклі for-each колекцію-копію, а видаляти елементи з оригінальної колекції.
| Код | Примітка |
|---|---|
|
Копію колекції створити дуже легко Цикл використовує ітератор колекції-копії. Елементи видаляються з колекції list. |
Копія колекції створюється досить швидко: елементи під час копіювання колекції не дублюються, у новій колекції будуть зберігатися посилання на ті ж елементи, що і в старій.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ