Iterator и Iterable
В Java обход коллекций унифицирован через два интерфейса:
Iterable<E>— «то, что можно обойти». Содержит единственный обязательный методiterator(). Любой класс, реализующийIterable, можно использовать в циклеfor-each.Iterator<E>— сам обходчик. Методы:hasNext(),next(),remove().
Collection наследует Iterable, поэтому все коллекции (List, Set, Queue) поддерживают for-each. Map — не Iterable, но через keySet()/values()/entrySet() тоже легко обходится.
ListIterator<E> — расширение для списков с двусторонним обходом и заменой элементов.
Контракт Iterator
interface Iterator<E> {
boolean hasNext();
E next(); // бросает NoSuchElementException, если элементов нет
void remove(); // опционально: удаляет последний возвращённый next()
}
Пример 1. Явный Iterator
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class IteratorBasic {
public static void main(String[] args) {
List<String> kits = new ArrayList<>();
kits.add("UNO");
kits.add("ESP32");
kits.add("Phobo");
Iterator<String> it = kits.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
}
Output:
UNO
ESP32
Phobo
Пример 2. Безопасное удаление через Iterator.remove
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
public class IteratorRemove {
public static void main(String[] args) {
List<Integer> values = new ArrayList<>(Arrays.asList(10, 25, 7, 42, 3));
Iterator<Integer> it = values.iterator();
while (it.hasNext()) {
if (it.next() < 10) {
it.remove();
}
}
System.out.println(values);
}
}
Output:
[10, 25, 42]
Пример 3. fail-fast поведение
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ConcurrentModificationException;
public class IteratorFailFast {
public static void main(String[] args) {
List<String> data = new ArrayList<>();
data.add("a"); data.add("b"); data.add("c");
try {
for (String s : data) {
if (s.equals("b")) {
data.remove(s); // структурное изменение во время обхода
}
}
} catch (ConcurrentModificationException e) {
System.out.println("Поймано: ConcurrentModificationException");
}
// Правильный способ:
Iterator<String> it = data.iterator();
while (it.hasNext()) {
if (it.next().equals("b")) it.remove();
}
System.out.println(data);
}
}
Output:
Поймано: ConcurrentModificationException
[a, c]
Пример 4. ListIterator — двусторонний обход
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ListIterator;
public class IteratorListIter {
public static void main(String[] args) {
List<Integer> values = new ArrayList<>(Arrays.asList(1, 2, 3, 4));
ListIterator<Integer> it = values.listIterator();
while (it.hasNext()) {
int v = it.next();
it.set(v * 10);
}
System.out.println("Вперёд: " + values);
StringBuilder sb = new StringBuilder("Назад:");
while (it.hasPrevious()) {
sb.append(' ').append(it.previous());
}
System.out.println(sb);
}
}
Output:
Вперёд: [10, 20, 30, 40]
Назад: 40 30 20 10
Пример 5. Свой Iterable
import java.util.Iterator;
import java.util.NoSuchElementException;
public class IteratorCustom {
static class Range implements Iterable<Integer> {
private final int from, to;
Range(int from, int to) { this.from = from; this.to = to; }
@Override
public Iterator<Integer> iterator() {
return new Iterator<>() {
int cur = from;
@Override public boolean hasNext() { return cur < to; }
@Override public Integer next() {
if (!hasNext()) throw new NoSuchElementException();
return cur++;
}
};
}
}
public static void main(String[] args) {
for (int v : new Range(2, 7)) {
System.out.print(v + " ");
}
System.out.println();
}
}
Output:
2 3 4 5 6
Пример 6. forEach и Stream
import java.util.Arrays;
import java.util.List;
public class IteratorForEach {
public static void main(String[] args) {
List<String> cmds = Arrays.asList("F", "L", "R", "S");
cmds.forEach(c -> System.out.println("cmd=" + c));
long count = cmds.stream().filter(c -> !c.equals("S")).count();
System.out.println("Движений: " + count);
}
}
Output:
cmd=F
cmd=L
cmd=R
cmd=S
Движений: 3
Подводные камни
Изменение коллекции во время
for-eachбезIterator.remove()— почти всегдаConcurrentModificationException.Iterator.remove()можно вызывать только один раз на каждыйnext().Iteratorодноразовый — после прохода создавайте новый.Не все
Iteratorподдерживаютremove()— некоторые бросаютUnsupportedOperationException(например, для неизменяемых коллекций).В многопоточном коде fail-fast не гарантирует обнаружение всех ошибок; используйте
CopyOnWriteArrayListили явную синхронизацию.
См. также
Примечание
Материал подготовлен на основе официального руководства Oracle The Collections Framework и распространяется на условиях Oracle Free Documentation License. Тексты переписаны своими словами, примеры кода — оригинальные.