Обход коллекций
Почти любая работа с коллекцией сводится к обходу её элементов: вывести, посчитать, отфильтровать. В Java для этого есть несколько способов, и у каждого своё место.
Цикл for-each
Самый простой способ — цикл for-each. Он работает с любой коллекцией: списком, множеством, очередью:
void main() {
List<String> names = List.of("Анна", "Олег", "Ира");
for (String name : names) {
IO.println("Привет, " + name);
}
}Результат:
Привет, Анна
Привет, Олег
Привет, ИраЧитается он так: «для каждого name из names».
Обход по индексу
Если нужен номер элемента, используют обычный цикл for с индексом. Это работает только для списков — у множества индексов нет:
void main() {
List<String> places = List.of("Анна", "Олег", "Ира");
for (int i = 0; i < places.size(); i++) {
IO.println((i + 1) + " место: " + places.get(i));
}
}Результат:
1 место: Анна
2 место: Олег
3 место: ИраМетод forEach()
У каждой коллекции есть метод forEach(). Ему передают действие в виде лямбды — короткой записи функции: слева от стрелки параметр, справа то, что с ним сделать:
void main() {
List<Integer> prices = List.of(10, 25, 7);
prices.forEach(price -> IO.println(price + " €"));
}Результат:
10 €
25 €
7 €Лямбды подробно разобраны в следующей главе, пока достаточно этой формы.
Обход словаря
Словарь — не коллекция элементов, а набор пар. Можно перебрать только ключи (keySet()), только значения (values()) или пары целиком. Удобнее всего — forEach() с двумя параметрами:
void main() {
Map<String, Integer> scores = new TreeMap<>(Map.of("Олег", 75, "Анна", 90));
for (String name : scores.keySet()) {
IO.println(name);
}
scores.forEach((name, score) -> IO.println(name + " → " + score));
}Результат:
Анна
Олег
Анна → 90
Олег → 75Удаление при обходе
Чтобы удалить элементы по условию, у коллекций есть метод removeIf(). Он принимает условие и сам удаляет все подходящие элементы:
void main() {
List<Integer> numbers = new ArrayList<>(List.of(1, 2, 3, 4, 5, 6));
numbers.removeIf(n -> n % 2 == 0);
IO.println(numbers);
}Результат:
[1, 3, 5]Более старый способ — объект Iterator: его методы hasNext() и next() перебирают элементы, а remove() безопасно удаляет текущий. Как написать свой итератор и научить for-each перебирать ваш класс, показано в уроке свой Iterable.
Частая ошибка
Внутри цикла for-each нельзя удалять элементы из той же коллекции. Список замечает, что его изменили во время обхода, и выбрасывает исключение:
void main() {
List<String> items = new ArrayList<>(List.of("хлеб", "молоко", "сыр", "чай"));
for (String item : items) {
if (item.equals("молоко")) {
items.remove(item);
}
}
}Результат:
Exception in thread "main" java.util.ConcurrentModificationExceptionИспользуйте items.removeIf(item -> item.equals("молоко")) или Iterator с его методом remove().