Какие операции поддерживает интерфейс 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 итераторы]]