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] — нуль видалено помилково

Стежте за точністю умов: одне хибне порівняння повністю змінює результат.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ