1. Введение
Итератор — это
один из многих способов обхода коллекции, и, как и у каждого варианта, у него есть свои плюсы и минусы.
Впервые он был представлен в Java 1.2 как замена Enumerations
и:
- введены улучшенные имена методов
- сделали возможным удаление элементов из коллекции, которую мы повторяем
- не гарантирует порядок итерации
В этом уроке мы рассмотрим простой интерфейс Iterator
, чтобы узнать, как мы можем использовать его различные методы.
Мы также проверим более надежное расширение ListIterator
, которое добавляет некоторые интересные функции.
2. Интерфейс итератора
Для начала нам нужно получить итератор
из коллекции
; это делается путем вызова метода iterator() .
Для простоты мы получим экземпляр Iterator
из списка:
List<String> items = ...
Iterator<String> iter = items.iterator();
Интерфейс Iterator
имеет три основных метода:
2.1. hasNext()
Метод hasNext()
можно использовать для проверки наличия хотя бы одного элемента для повторения.
Он предназначен для использования в качестве условия в циклах while :
while (iter.hasNext()) {
// ...
}
2.2. следующий()
Метод next()
можно использовать для перехода к следующему элементу и его получения:
String next = iter.next();
Хорошей практикой является использование hasNext()
перед попыткой вызова next()
.
Итераторы
для коллекций
не гарантируют итерацию в каком-либо конкретном порядке, если это не предусмотрено конкретной реализацией.
2.3. удалять()
Наконец, если мы хотим удалить текущий элемент из коллекции, мы можем использовать метод удаления:
iter.remove();
Это безопасный способ удаления элементов при переборе коллекции без риска ConcurrentModificationException.
2.4. Полный пример итератора
Теперь мы можем объединить их все и посмотреть, как мы используем три метода вместе для фильтрации коллекции:
while (iter.hasNext()) {
String next = iter.next();
System.out.println(next);
if( "TWO".equals(next)) {
iter.remove();
}
}
Вот как мы обычно используем итератор:
мы заранее проверяем, есть ли другой элемент, мы извлекаем его, а затем выполняем с ним какое-то действие.
2.5. Итерация с лямбда-выражениями
Как мы видели в предыдущих примерах, использовать итератор
очень многословно, когда мы просто хотим просмотреть все элементы и что-то с ними сделать.
Начиная с Java 8 у нас есть метод forEachRemaining
, который позволяет использовать лямбда-выражения для обработки оставшихся элементов:
iter.forEachRemaining(System.out::println);
3. Интерфейс ListIterator
ListIterator
— это расширение, добавляющее новые функции для перебора списков:
ListIterator<String> listIterator = items.listIterator(items.size());
Обратите внимание, как мы можем указать начальную позицию, которая в данном случае является концом списка.
3.1. hasPrevious()
и предыдущий()
ListIterator
можно использовать для обратного обхода, поэтому он предоставляет эквиваленты hasNext()
и next()
:
while(listIterator.hasPrevious()) {
String previous = listIterator.previous();
}
3.2. следующийИндекс()
и предыдущийИндекс()
Кроме того, мы можем перемещаться по индексам, а не по фактическим элементам:
String nextWithIndex = items.get(listIterator.nextIndex());
String previousWithIndex = items.get(listIterator.previousIndex());
Это может оказаться очень полезным, если нам нужно знать индексы объектов, которые мы в данный момент модифицируем, или если мы хотим вести учет удаленных элементов.
3.3. добавлять()
Метод add
, который, как следует из названия, позволяет нам добавить элемент перед элементом, который будет возвращен функцией next()
, и после элемента, возвращенного функцией previous():
listIterator.add("FOUR");
3.4. установлен()
Последний метод, о котором стоит упомянуть, это set(),
который позволяет нам заменить элемент, возвращенный при вызове, на next()
или previous()
:
String next = listIterator.next();
if( "ONE".equals(next)) {
listIterator.set("SWAPPED");
}
Важно отметить, что это может быть выполнено только в том случае, если не было сделано никаких предыдущих вызовов add()
или remove()
.
3.5. Полный пример ListIterator
Теперь мы можем объединить их все, чтобы сделать полный пример:
ListIterator<String> listIterator = items.listIterator();
while(listIterator.hasNext()) {
String nextWithIndex = items.get(listIterator.nextIndex());
String next = listIterator.next();
if("REPLACE ME".equals(next)) {
listIterator.set("REPLACED");
}
}
listIterator.add("NEW");
while(listIterator.hasPrevious()) {
String previousWithIndex
= items.get(listIterator.previousIndex());
String previous = listIterator.previous();
System.out.println(previous);
}
В этом примере мы начинаем с получения ListIterator
из List
, затем мы можем получить следующий элемент либо по индексу , который не увеличивает внутренний текущий элемент итератора, либо с помощью вызова next
.
Затем мы можем заменить конкретный элемент на set
и вставить новый с помощью add.
Достигнув конца итерации, мы можем вернуться назад, чтобы изменить дополнительные элементы или просто распечатать их снизу вверх.
4. Вывод
Интерфейс Iterator
позволяет нам изменять коллекцию во время ее обхода, что сложнее с простым оператором for/while. Это, в свою очередь, дает нам хороший шаблон, который мы можем использовать во многих методах, которые требуют только обработки коллекций, сохраняя при этом хорошую связность и низкую связанность.
Наконец, как всегда, полный исходный код доступен на GitHub .