JavaRush /Курсы /JAVA 25 SELF /Фильтрация элементов коллекций

Фильтрация элементов коллекций

JAVA 25 SELF
28 уровень , 0 лекция
Открыта

1. Введение

В программировании мы постоянно сталкиваемся с ситуацией, когда из большого набора данных нужно выбрать только нужные элементы — это и есть фильтрация. Хотите оставить только чётные числа, найти строки, содержащие слово "Java", или выбрать пользователей старше 18 лет — всё это задачи фильтрации.

В Java подобные операции встречаются на каждом шагу, поэтому важно уверенно владеть различными способами их выполнения и понимать их особенности.

2. Фильтрация с помощью цикла

Начнём с самого базового способа — обычный цикл for. Такой подход называют императивным, потому что вы явно указываете, что и как делать. Он хорошо читается и прост для понимания.

Пример: оставить только чётные числа

import java.util.*;

public class FilterExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        List<Integer> evenNumbers = new ArrayList<>(); // Создаём новый список для результата

        for (Integer n : numbers) {
            if (n % 2 == 0) { // Проверяем условие: чётное число
                evenNumbers.add(n);
            }
        }

        System.out.println("Чётные числа: " + evenNumbers);
    }
}

Результат:

Чётные числа: [2, 4, 6, 8, 10]

Здесь важно помнить: исходная коллекция (numbers) не меняется. Мы формируем новый список-результат.

Пример: фильтрация строк по подстроке

List<String> words = Arrays.asList("java", "python", "javascript", "kotlin", "c++");
List<String> javaWords = new ArrayList<>();

for (String word : words) {
    if (word.contains("java")) {
        javaWords.add(word);
    }
}
System.out.println(javaWords); // [java, javascript]

Тот же принцип: пробегаемся по списку строк и проверяем наличие подстроки с помощью метода contains.

3. Удаление элементов из коллекции: почему не всё так просто?

В чём подвох?

Иногда хочется удалить из исходной коллекции все ненужные элементы. Но если попытаться сделать это во время перебора в цикле for-each, вы получите исключение ConcurrentModificationException.

List<Integer> numbers = new ArrayList<>(Arrays.asList(1, -2, 3, -4, 5));

for (Integer n : numbers) {
    if (n < 0) {
        numbers.remove(n); // ОПАСНО! ConcurrentModificationException!
    }
}

Результат:

Exception in thread "main" java.util.ConcurrentModificationException

Коллекции «не любят», когда их модифицируют во время такого перебора — итерация ломается.

Как правильно удалять элементы из коллекции?

Способ 1: использовать Iterator.remove()

List<Integer> numbers = new ArrayList<>(Arrays.asList(1, -2, 3, -4, 5));

Iterator<Integer> it = numbers.iterator();
while (it.hasNext()) {
    Integer n = it.next();
    if (n < 0) {
        it.remove(); // Безопасно удаляем!
    }
}
System.out.println(numbers); // [1, 3, 5]

Здесь мы создаём итератор методом iterator(), двигаемся по коллекции через hasNext() и next(), а ненужные элементы удаляем вызовом it.remove().

Способ 2: создать новый список только с нужными элементами

List<Integer> numbers = Arrays.asList(1, -2, 3, -4, 5);
List<Integer> positive = new ArrayList<>();

for (Integer n : numbers) {
    if (n >= 0) {
        positive.add(n);
    }
}
System.out.println(positive); // [1, 3, 5]

Мы не трогаем исходную коллекцию и собираем новый список. Подход безопасный, наглядный и легко расширяется при усложнении условий.

Способ 3: использовать removeIf (Java 8+)

List<Integer> numbers = new ArrayList<>(Arrays.asList(1, -2, 3, -4, 5));
numbers.removeIf(n -> n < 0);
System.out.println(numbers); // [1, 3, 5]

Одной строкой передаём условие: «удали всё, что меньше нуля». Внутренние детали безопасной модификации берёт на себя коллекция.

Итого: для удаления элементов не используйте for-each. Выбирайте между Iterator.remove(), созданием нового списка или лаконичным removeIf.

4. Когда какой способ использовать для фильтрации?

Императивный подход через обычный цикл удобен, когда вам нужно не только отобрать элементы, но и сразу что-то с ними сделать (например, вывести или преобразовать). Он прост и прозрачен.

Если речь идёт именно об удалении из исходной коллекции, избегайте удаления в for-each. Используйте Iterator.remove() для пошагового контроля или современный removeIf() — максимально короткий и выразительный способ.

Пример: есть список слов, нужно удалить все слова короче четырёх символов:

List<String> words = new ArrayList<>(Arrays.asList("Java", "is", "fun", "awesome", "code"));
words.removeIf(word -> word.length() < 4);
System.out.println(words); // [Java, awesome, code]

Метод removeIf принимает предикат — правило фильтрации — и удаляет всё, что ему соответствует.

5. Типичные ошибки при фильтрации коллекций

Ошибка №1: попытка удалить элементы из коллекции в цикле for-each.
Такой код приведёт к ConcurrentModificationException:

for (Integer n : numbers) {
    if (n < 0) {
        numbers.remove(n); // БУМ! ConcurrentModificationException
    }
}

Правильные варианты: используйте Iterator.remove() или removeIf.

Ошибка №2: неверно сформулированное условие фильтрации.
Нужно удалить только отрицательные числа:

List<Integer> numbers = new ArrayList<>(Arrays.asList(-3, -1, 0, 2, 4));
numbers.removeIf(n -> n < 0);
System.out.println(numbers); // [0, 2, 4]

Но если по ошибке написать «неположительные» и использовать <=, то исчезнет и ноль:

numbers.removeIf(n -> n <= 0); // Результат: [2, 4] — ноль удалён по ошибке

Следите за точностью условий: одно неверное сравнение полностью меняет результат.

1
Задача
JAVA 25 SELF, 28 уровень, 0 лекция
Недоступна
Очистка досье агентов от "негативной" информации 🕵️‍♀️
Очистка досье агентов от "негативной" информации 🕵️‍♀️
1
Задача
JAVA 25 SELF, 28 уровень, 0 лекция
Недоступна
Упорядочиваем названия животных для новой энциклопедии 🦒
Упорядочиваем названия животных для новой энциклопедии 🦒
Комментарии (7)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Александр Уровень 50
12 ноября 2025
Помогите, пожалуйста, разъясните ситуацию. Вот решение из лекции:

List<Integer> skills = new ArrayList<>(Arrays.asList(5, -3, 8, -1, 0, 4, -7));
А вот решение, при котором метод it.remove() выбрасывает исключение: Exception in thread "main" java.lang.UnsupportedOperationException: remove.

List<Integer> skills = Arrays.asList(5, -3, 8, -1, 0, 4, -7);
В первом случае arraylist создается явно, под капотом в него копируются элементы из Arrays.asList(5, -3, 8, -1, 0, 4, -7). Во втором случае skills ссылается на arraylist, созданный методом Arrays.asList(5, -3, 8, -1, 0, 4, -7). Но данный список модифицировать итератором уже не получается. Почему?
Xaxatumba Уровень 38
15 ноября 2025
Java Collections расширяют интерфейс Iterable, но это не означает, что List, Set и Queue поддерживают итератор. ArrayList как раз поддерживает ошибки нет. А вот List<Integer> не знает о таком методе вот тебе и ошибка.
Александр Уровень 50
17 ноября 2025

А вот List<Integer> не знает о таком методе вот тебе и ошибка.
Не соглашусь. Collection extends Iterator. List extends Collection. То есть List знает метод iterator(). Именно поэтому skills.iterator() компилируется нормально. Я не нахожу ответа вот на эту ситуацию: Метод Arrays.asList под капотом создает объект типа ArrayList.

public static <T> List<T> asList(T... a) {
        return new ArrayList<>(a);
    }
Почему же этот объект нельзя итерировать напрямую, а приходится создавать его копию new ArrayList<>(Arrays.asList(5, -3, 8, -1, 0, 4, -7));
Big198801 Уровень 33
8 декабря 2025
Arrays.asList() возвращает список фиксированного размера, и при попытке удалить элемент выбросит UnsupportedOperationException.
Sergey Lunev Уровень 41
10 декабря 2025
Во втором случае он ссылается на список фиксированного размера, возвращаемый Arrays.asList(). Это не настоящий ArrayList, а внутренний класс Arrays.ArrayList. Он не поддерживает операции добавления/удаления
Александр Уровень 50
11 декабря 2025

Это не настоящий ArrayList, а внутренний класс Arrays.ArrayList. Он не поддерживает операции добавления/удаления
Действительно, внутренний класс прямо под методом, как его не заметил... Вот теперь вопрос снят окончательно. Сергей, спасибо!
Andrey Уровень 1
23 сентября 2025
28