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] — нуль видалено помилково
Стежте за точністю умов: одне хибне порівняння повністю змінює результат.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ