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
і практично завжди використовують його, коли потрібно обійти всі елементи колекції.
З використанням циклу for-each
навіть обхід списку ArrayList
буде коротшим:
Цикл for-each | Цикл for |
---|---|
|
|
4. Видалення елемента в циклі for-each
Цикл for-each
має один недолік: він не вміє правильно видаляти елементи. Якщо ви напишете такий код, отримаєте помилку.
Код | Примітка |
---|---|
|
Під час видалення станеться помилка! |
Це дуже гарний і зрозумілий код, але працювати він не буде.
Не можна змінювати колекцію, поки ви обходите її за допомогою ітератора.
Є три способи подолати це обмеження.
1 Використання іншого циклу
Якщо ви обходите колекцію ArrayList
, можна скористатися звичайним циклом із лічильником i
.
Код |
---|
|
Однак цей варіант не підходить для колекцій HashSet
і HashMap
.
2 Явне використання ітератора
Можна в явний спосіб скористатися ітератором і його методом remove()
.
Працюючий варіант | Непрацюючий варіант |
---|---|
|
|
Зверніть увагу, що метод remove()
ми викликаємо для об'єкта-ітератора! Ітератор «знатиме» про видалення елемента і зможе правильно обробити цю ситуацію.
3 Використання копії колекції
Можна також створити копію колекції та використовувати її в циклі for-each
, а видаляти елементи з колекції-оригіналу.
Код | Примітка |
---|---|
|
Копію колекції створити дуже легко У циклі використовується ітератор колекції-копії. Елементи видаляються з колекції list . |
Копія колекції створюється досить швидко: під час копіювання елементи колекції не дублюються — у новій колекції зберігатимуться посилання на ті самі елементи, що й у старій.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ