Java Collections Framework: иерархия, порядок и выбор контейнера
Автор: Казачкин Даниил Михайлович · Обновлено
Java Collections Framework — семейство интерфейсов и реализаций для хранения и обработки групп объектов. Чтобы выбрать контейнер, сначала решите, нужны ли повторения, индексы,…
Java Collections Framework — семейство интерфейсов и реализаций для хранения и обработки групп объектов. Чтобы выбрать контейнер, сначала решите, нужны ли повторения, индексы, уникальность, очередь или поиск по ключу; имя конкретного класса выбирают после этого.
Иерархия для Java 25
Collection — интерфейс группы элементов. Collections с буквой s — служебный класс со статическими методами. Map представляет соответствия ключ–значение и не наследует Collection. Представления keySet, values и entrySet позволяют обходить части Map как коллекции. Обзор устройства.
В таблице перечислены основные прямые связи интерфейсов. Она учитывает sequenced-интерфейсы, появившиеся в Java 21; старые схемы без них полезны для истории, но неполны для выбранного JDK.
| Интерфейс | Расширяет | Существенное свойство |
|---|---|---|
Collection<E> | Iterable<E> | Группа элементов с обходом |
SequencedCollection<E> | Collection<E> | Определённый порядок обхода и два конца |
List<E> | SequencedCollection<E> | Позиции и индексы, возможны повторы |
Set<E> | Collection<E> | Нет равных повторяющихся элементов |
SequencedSet<E> | Set<E>, SequencedCollection<E> | Уникальность и определённый порядок |
SortedSet<E> | SequencedSet<E> | Порядок задаётся сравнением |
NavigableSet<E> | SortedSet<E> | Поиск соседних по порядку значений |
Queue<E> | Collection<E> | Очередь с операциями извлечения |
Deque<E> | Queue<E>, SequencedCollection<E> | Работа с обоими концами |
Map<K,V> | Отдельная ветвь | Один ключ соответствует одному значению |
SequencedMap<K,V> | Map<K,V> | Определённый порядок записей |
SortedMap<K,V> | SequencedMap<K,V> | Ключи упорядочены сравнением |
NavigableMap<K,V> | SortedMap<K,V> | Поиск соседних ключей и диапазонов |
Таблица — граф с несколькими родителями, а не дерево с единственной цепочкой. Например, Deque одновременно является очередью и упорядоченной коллекцией. SequencedCollection, SequencedSet, SequencedMap.
Выбор реализации меняет гарантии
| Задача | Обычный выбор | На что обратить внимание |
|---|---|---|
| Список и доступ по индексу | ArrayList | Повторы допустимы; вставка в середину сдвигает элементы |
| Двусвязный список | LinkedList | Реализует List и Deque; доступ по индексу требует обхода |
| Уникальные значения | HashSet | Порядок обхода не обещан |
| Уникальные значения в порядке добавления | LinkedHashSet | Удаление и повторное добавление меняют место |
| Отсортированные уникальные значения | TreeSet | Уникальность определяется сравнением |
| Значение по ключу | HashMap | Порядок записей не обещан |
| Записи в порядке добавления | LinkedHashMap | Есть также отдельный режим порядка доступа |
| Отсортированные ключи | TreeMap | Требуется согласованное сравнение ключей |
| Стек или очередь с двух концов | ArrayDeque | Не разрешает null |
Порядок добавления и сортировка решают разные задачи. Если посетители зарегистрировались как web, затем core, сортировка названий отделов изменит порядок, а LinkedHashMap по умолчанию его сохранит. Не делайте вывод о гарантии HashMap по одному удачному запуску. Map и порядок ключей.
Один набор данных в трёх контейнерах
Полный пример CollectionChoices.java работает на Java 25:
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
public class CollectionChoices {
public static void main(String[] args) {
List<String> visits = new ArrayList<>(List.of("web", "core", "web"));
Set<String> departments = new LinkedHashSet<>(visits);
Map<String, Integer> counts = new LinkedHashMap<>();
for (String name : visits) {
counts.put(name, counts.getOrDefault(name, 0) + 1);
}
var queue = new ArrayDeque<>(visits);
System.out.println(visits);
System.out.println(departments);
System.out.println(counts);
System.out.println(new TreeMap<>(counts));
System.out.println(queue.removeFirst());
System.out.println(queue);
}
}Сборка: javac -encoding UTF-8 --release 25 CollectionChoices.java; запуск: java CollectionChoices. Вывод:
[web, core, web]
[web, core]
{web=2, core=1}
{core=1, web=2}
web
[core, web]Здесь список хранит каждое посещение, множество — названия отделов, а Map — агрегат. Копирование в TreeMap создаёт другое представление отчёта; оно не меняет исходный counts. Тип переменной Map позволяет не привязывать остальную программу к способу хранения.
Изменяемость и null — отдельные контракты
Наличие метода add в интерфейсе не обещает, что конкретный объект разрешает добавление. Фабрики List.of, Set.of, Map.of создают немодифицируемые контейнеры и запрещают null. ArrayList и HashSet допускают null-элементы; HashMap допускает null-ключ и null-значения. Для TreeMap и TreeSet с естественным порядком null-ключи и null-элементы недопустимы; возможность иного поведения зависит от comparator. Не переносите контракт одного контейнера на другой. Фабрики коллекций, HashMap, TreeMap.
Немодифицируемая коллекция также не делает вложенные объекты глубоко неизменяемыми. Если внутрь положить изменяемый Stock из предыдущего урока, его методы остаются доступными. А представление вроде map.values() связано с исходной Map: это не автоматически независимая копия.
Практика выбора структуры
Для каждого сценария назовите интерфейс, реализацию и необходимую гарантию: история всех посещений; уникальные логины по первому появлению; расписание по отсортированному времени; задания в порядке поступления. Затем измените пример так, чтобы третье посещение стало qa, и заранее предскажите все строки отчёта. Если задача требует повторов, замена списка множеством будет потерей данных, даже когда код компилируется.
Частые вопросы
Почему Map не является Collection?
У Map основной контракт — соответствие ключа значению, а не отдельный элемент списка. Для работы с её содержимым предусмотрены коллекционные представления; поэтому использовать общий обход можно без включения Map в ветку Collection.
SequencedCollection означает, что элементы отсортированы?
Нет. Он задаёт определённый порядок обхода. В списке это порядок позиций, а в сортированном множестве — порядок сравнения. Упорядоченность и сортировка не являются синонимами.