Які операції підтримує інтерфейс Collection?
Якщо проект на Java 8 — використовуйте coll.toArray(new String[0]). JVM оптимізує це так само ефективно.
🟢 Junior Level
Collection — базовий інтерфейс для List, Set, Queue.
⚠️ НЕ плутати:
Collection(без ‘s’) — інтерфейс (List, Set, Queue).Collections(з ‘s’) — утилітарний клас зі static-методами (sort,synchronizedList,unmodifiableList). Це різні речі!
Основні методи:
Collection<String> coll = new ArrayList<>();
// Додавання
coll.add("A");
coll.addAll(List.of("B", "C"));
// Перевірка
coll.contains("A"); // true
coll.containsAll(...); // true
coll.isEmpty(); // false
coll.size(); // 3
// Видалення
coll.remove("A");
coll.removeAll(...);
coll.clear();
// Перетворення
Object[] arr = coll.toArray();
String[] strArr = coll.toArray(String[]::new); // Java 11+
🟡 Middle Level
Групи методів
Модифікація:
add(e) // → true якщо змінилась
addAll(c) // → true якщо змінилась
remove(e) // → true якщо видалив
removeAll(c) // Видалити всі з c
retainAll(c) // Залишити тільки з c (перетин)
clear() // Очистити
Java 8+ функціональні:
// removeIf — видалення за умовою
list.removeIf(e -> e.startsWith("A")); // O(n) для ArrayList!
// stream / parallelStream
coll.stream()
.filter(e -> e.length() > 3)
.map(String::toUpperCase)
.collect(Collectors.toList());
Складність contains
// ArrayList: O(n)
list.contains("A"); // Перебір!
// HashSet: O(1)
set.contains("A"); // Хеш!
// containsAll для двох списків: O(n × m)!
list1.containsAll(list2); // Для кожного з list2 перебір list1!
toArray
// Java 11+: найсучасніший спосіб
String[] arr = list.toArray(String[]::new);
// Старий спосіб (теж працює)
String[] arr = list.toArray(new String[0]);
// → JVM оптимізує, не потрібно створювати масив потрібного розміру!
Якщо проект на Java 8 — використовуйте coll.toArray(new String[0]). JVM оптимізує це так само ефективно.
🔴 Senior Level
Коли НЕ використовувати parallelStream
- Колекція < 10K елементів — overhead форка > вигода
- Операції швидкі (арифметика, getters)
- Порядок важливий — parallelStream не гарантує порядок
- Є side-ефекти — race conditions
removeIf оптимізація
// ArrayList.removeIf:
// 1. Знаходить перший елемент для видалення
// 2. До нього — НЕ чіпає
// 3. Після — ОДИН System.arraycopy
// → O(n), мінімальні записи!
// VS iterator.remove() в циклі:
// → System.arraycopy КОЖЕН раз
// → O(n²)!
Bulk operations складність
// list1.removeAll(list2)
// Якщо list2 = ArrayList: O(n × m)
// Якщо list2 = HashSet: O(n + m)
// → Завжди конвертуйте в Set для bulk операцій!
Set<String> set = new HashSet<>(list2);
list1.removeAll(set); // O(n + m)
Для середніх і великих колекцій — конвертація в Set вигідна. Для маленьких (< 10 елементів) overhead створення HashSet може перевищити вигоду.
Spliterator характеристики
Spliterator<String> spliterator = coll.spliterator();
// Прапорці оптимізації:
// IMMUTABLE → колекція не змінюється
// CONCURRENT → можна змінювати паралельно
// DISTINCT → всі елементи унікальні
// SORTED → відсортовано
// SIZED → відомий точний розмір
// Stream API використовує ці прапорці!
set.stream()
.distinct() // → Нічого не робить! Вже DISTINCT
.count(); // → O(1) тільки коли Stream знає точний розмір і немає операцій, що змінюють кількість елементів. distinct() все одно вимагає обходу.
GC Pressure
// toArray() створює КОПІЮ всіх даних!
Object[] copy = collection.toArray(); // O(n) пам'ять
// Для великих колекцій → GC тиск
// → Використовуйте stream замість копіювання
collection.stream().forEach(this::process);
toArray створює повну копію. Уникайте якщо: (1) Колекція > 100K елементів (GC pressure). (2) Ви тільки читаєте (використовуйте stream/forEach). Припустимо якщо: (1) Потрібен масив для legacy API. (2) Колекція маленька.
Production Experience
Реальний сценарій: containsAll O(n²)
// 10,000 елементів в обох списках
list1.containsAll(list2); // 10,000 × 10,000 = 100 млн операцій!
// Рішення:
Set<String> set = new HashSet<>(list2);
list1.containsAll(set); // 10,000 × O(1) = 10,000 операцій
// → Прискорення в 10,000 разів!
Best Practices
- removeIf → O(n) для ArrayList
- toArray(T[]::new) → Java 11+ стиль
- containsAll з Set → O(n+m) замість O(n×m)
- Spliterator характеристики → оптимізації Stream
- Уникайте toArray для великих колекцій (GC)
- retainAll = перетин множин
- addAll = об’єднання
Резюме для Senior
- removeIf = O(n), оптимізований всередині
- Bulk ops → конвертуйте в Set
- toArray → копія, GC pressure
- Spliterator → прапорці для Stream оптимізацій
- contains → O(n) для List, O(1) для Set
- Java 11+ → toArray(T[]::new)
🎯 Шпаргалка для інтерв’ю
Обов’язково знати:
Collection(інтерфейс) !=Collections(утилітарний клас) — не плутати на інтерв’ю- Базові групи: модифікація (
add,remove,retainAll), перевірка (contains,isEmpty), перетворення (stream,toArray) removeIf()— O(n) для ArrayList (одинSystem.arraycopy), кращеiterator.remove()O(n^2)containsAll/removeAllдля двох ArrayList = O(n × m); конвертація одного в Set = O(n + m)toArray(T[]::new)(Java 11+) — сучасний стиль; JVM оптимізуєnew T[0]так само ефективноtoArray()створює повну копію — GC pressure для великих колекцій (>100K елементів)Spliteratorпрапорці (DISTINCT, SORTED, SIZED) — внутрішні оптимізації Stream API
Часті уточнюючі запитання:
- Чому
containsAllдвох списків з 10K елементів = 100 млн операцій? — Для кожного елемента list2 перебирається list1: 10K × 10K. Рішення:new HashSet(list2). - Коли
parallelStreamНЕ варто використовувати? — Колекція < 10K елементів, швидкі операції, важливий порядок, є side-ефекти. - Навіщо конвертувати в Set перед
removeAll? — HashSet.contains = O(1); замість O(n × m) отримуємо O(n + m). - Що роблять прапорці Spliterator? — Підказки Stream API для оптимізації: DISTINCT —
distinct()noop, SIZED —count()= O(1).
Червоні прапорці (НЕ говорити):
- «Collection і Collections — одне і те ж» — ні, Collection = інтерфейс, Collections = утилітарний клас
- «
containsдля ArrayList = O(1)» — ні, O(n), перебір всіх елементів - «
toArray()не створює копію» — ні, це повна копія даних в новий масив - «
parallelStreamзавжди швидший за звичайнийstream» — ні, overhead форка для малих колекцій перевищує вигоду
Пов’язані теми:
- [[28. Як правильно видаляти елементи під час ітерації]]
- [[23. Що таке Collections.unmodifiableList()]]
- [[26. Що таке fail-fast та fail-safe ітератори]]