Коллекции ·
‹ Previous Next ›
⏱ 5 min read Modified: 2026-10-21

Коллекции в Java: Collection Framework и иерархия коллекций

Попробуйте написать Collection<String> names = new HashMap<String, String>(); — компилятор ответит incompatible types: HashMap<String,String> cannot be converted to Collection<String>. HashMap входит в Collection Framework, но интерфейс Collection не реализует. Чтобы такие сюрпризы не случались, сначала нужно увидеть иерархию коллекций целиком.

Collection Framework (Java Collections Framework) — это набор интерфейсов, реализаций и алгоритмов из пакета java.util для хранения и обработки групп объектов: списков, множеств, очередей и отображений «ключ–значение». В отличие от массива, размер коллекции меняется динамически, а готовые методы позволяют искать, сортировать и фильтровать элементы без ручного кода. Коллекции появились в J2SE 1.2 (1998), обобщённые типы в них — в Java 5.

Из чего состоит Collection Framework

Collection Framework в Java состоит из трёх частей: интерфейсов, которые описывают поведение коллекции (список, множество, очередь, отображение), реализаций — готовых классов вроде ArrayList и HashMap, и алгоритмов — статических методов для сортировки, поиска и перемешивания.

Часть Что это Примеры Где разбирается
Интерфейсы Контракт: какие операции поддерживает коллекция Collection, List, Set, Queue, Deque, Map Интерфейс Collection
Реализации Классы с конкретной структурой данных внутри ArrayList, LinkedList, HashSet, TreeSet, ArrayDeque, HashMap Уроки по каждому классу — см. карту модуля
Алгоритмы Статические методы классов Collections и Arrays Collections.sort(), binarySearch(), shuffle(), max() Методы класса Collections

Переменную принято объявлять типом интерфейса, а реализацию указывать только в new: List<String> names = new ArrayList<>();. Тогда реализацию можно заменить одной строкой, не трогая остальной код. Сами структуры данных (список, стек, очередь, множество) без привязки к Java описаны в уроке Структуры данных в Java.

Иерархия коллекций Java

Иерархия коллекций Java состоит из двух независимых веток. Первая начинается с интерфейса Collection (он расширяет Iterable) и делится на List, Set и Queue. Вторая — интерфейс Map для пар «ключ–значение»: он входит в Collection Framework, но Collection не наследует.

Иерархия коллекций Java: интерфейсы Collection, List, Set, Queue, Map и их реализации

Та же иерархия в текстовом виде (стрелка → означает «расширяет / реализует», после тире указаны основные классы):

Iterable<T>
└── Collection<E>
    ├── List<E>            — ArrayList, LinkedList, Vector → Stack
    ├── Set<E>             — HashSet → LinkedHashSet
    │   └── SortedSet<E> → NavigableSet<E>   — TreeSet
    └── Queue<E>           — PriorityQueue
        └── Deque<E>       — ArrayDeque, LinkedList

Map<K,V>                   — HashMap → LinkedHashMap, Hashtable
└── SortedMap<K,V> → NavigableMap<K,V>  — TreeMap

Обратите внимание: LinkedList встречается в двух ветках — он реализует и List, и Deque. Vector, Stack и Hashtable — классы из Java 1.0, встроенные в иерархию задним числом; в новом коде их не используют (урок Legacy Classes).

Что изменилось в Java 21

В Java 21 (JEP 431) в иерархию добавлены интерфейсы SequencedCollection, SequencedSet и SequencedMap для коллекций с определённым порядком. Их расширяют List, Deque, SortedSet, SortedMap, а реализуют также LinkedHashSet и LinkedHashMap. Отсюда методы getFirst(), getLast(), removeFirst(), removeLast() и reversed() у обычного ArrayList. На Java 17 вызов list.getFirst() не скомпилируется — там пишут list.get(0).

Основные интерфейсы коллекций

Основных интерфейсов коллекций шесть: List (упорядоченный список с индексами), Set (множество без дубликатов), SortedSet/NavigableSet (отсортированное множество), Queue (очередь), Deque (двусторонняя очередь и стек) и Map (пары «ключ–значение»). Различаются они тем, допускают ли дубликаты и в каком порядке отдают элементы.

Интерфейс Что моделирует Дубликаты Порядок элементов Урок
Collection Корень ветки: add(), remove(), contains(), size(), iterator() Зависит от наследника Зависит от наследника Collection
List Последовательность с доступом по индексу Да Порядок вставки List и ArrayList
Set Множество уникальных элементов Нет (add() вернёт false) Не гарантирован (HashSet) или порядок вставки (LinkedHashSet) Set, HashSet
SortedSet / NavigableSet Отсортированное множество с поиском ближайших элементов Нет По возрастанию или по Comparator SortedSet и TreeSet
Queue Очередь: добавление в хвост, извлечение из головы Да FIFO или по приоритету (PriorityQueue) Queue и Deque
Deque Двусторонняя очередь; заменяет класс Stack Да FIFO или LIFO — с обоих концов ArrayDeque
Map Отображение уникальных ключей на значения Ключи — нет, значения — да Зависит от реализации Map

Как сравниваются элементы в отсортированных коллекциях, объясняет урок Comparable и Comparator, а как их перебирать — урок Iterator.

Какую реализацию выбрать

Реализацию выбирают по трём вопросам: нужен ли доступ по индексу, важен ли порядок элементов и нужна ли сортировка. В большинстве задач хватает ArrayList, HashSet, HashMap и ArrayDeque. Остальные классы нужны, когда требуется особый порядок или быстрые вставки на концах списка.

Класс Внутри Порядок null Основные операции
ArrayList Динамический массив Вставки Да get(i) O(1), добавление в конец O(1) в среднем, вставка в середину O(n)
LinkedList Двусвязный список Вставки Да get(i) O(n), добавление/удаление на концах O(1)
ArrayDeque Кольцевой массив Вставки (с обоих концов) Нет — NullPointerException Операции на концах O(1)
PriorityQueue Двоичная куча По приоритету при извлечении Нет — NullPointerException offer()/poll() O(log n), peek() O(1)
HashSet Хеш-таблица (внутри HashMap) Не гарантирован Один null add()/contains() O(1) в среднем
LinkedHashSet Хеш-таблица + связный список Вставки Один null O(1) в среднем
TreeSet Красно-чёрное дерево Сортировка Нет — NullPointerException при естественном порядке O(log n)
HashMap Хеш-таблица Не гарантирован Один null-ключ, любые null-значения get()/put() O(1) в среднем
LinkedHashMap Хеш-таблица + связный список Вставки Как у HashMap O(1) в среднем
TreeMap Красно-чёрное дерево Сортировка ключей null-ключ — NullPointerException при естественном порядке O(log n)

Упорядоченные и отсортированные коллекции

Столбец «Порядок» в таблице описывает два разных свойства. Упорядоченная (ordered) коллекция перебирает элементы в предсказуемом порядке, который не зависит от их значений: по индексу или по порядку вставки. Отсортированная (sorted) коллекция сама расставляет элементы по их значениям — в естественном порядке (Comparable) или по правилу Comparator.

Сочетание Возможно? Примеры
Неупорядоченная и неотсортированная Да HashSet, HashMap
Упорядоченная, но неотсортированная Да ArrayList, LinkedList, LinkedHashSet, LinkedHashMap
Упорядоченная и отсортированная Да TreeSet, TreeMap
Отсортированная, но неупорядоченная Нет — сортировка сама задаёт порядок, поэтому любая отсортированная коллекция упорядочена
Set<String> linked = new LinkedHashSet<>(List.of("pear", "apple", "fig"));
Set<String> sorted = new TreeSet<>(List.of("pear", "apple", "fig"));
System.out.println(linked); // [pear, apple, fig] — порядок вставки
System.out.println(sorted); // [apple, fig, pear] — по алфавиту

Если важно, в каком порядке элементы вернутся при обходе, «упорядоченной» коллекции может оказаться недостаточно: LinkedHashSet сохранит порядок добавления, но не отсортирует данные. PriorityQueue — особый случай: poll() всегда отдаёт наименьший элемент, но итератор обходит кучу без сортировки.

Почему LinkedList проигрывает ArrayList при доступе по индексу, разобрано в уроке LinkedList, а зачем элементам HashSet и ключам HashMap методы equals() и hashCode() — в уроке Set и HashSet.

Обобщённые коллекции

Все интерфейсы и классы Collection Framework обобщённые (с Java 5): тип элементов указывают в угловых скобках, например List<String>. Компилятор не даст положить в такую коллекцию объект другого типа, а при чтении не потребуется приведение типа. Коллекция без параметра (raw type) компилируется только с предупреждением и ломается уже во время выполнения.

import java.util.ArrayList;
import java.util.List;

public class RawListExample {
    public static void main(String[] args) {
        List raw = new ArrayList();      // raw type: компилятор выдаст только warning
        raw.add("Java");
        raw.add(42);                     // ошибки нет — тип элементов не проверяется

        for (Object item : raw) {
            String s = (String) item;    // на втором элементе — исключение
            System.out.println(s.toUpperCase());
        }
    }
}

Вывод:

JAVA
Exception in thread "main" java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String (java.lang.Integer and java.lang.String are in module java.base of loader 'bootstrap')
	at RawListExample.main(RawListExample.java:11)

С объявлением List<String> list = new ArrayList<>(); строка list.add(42) не скомпилируется — ошибка найдена до запуска. Примитивы (int, double) в коллекции не кладут: используется класс-обёртка (List<Integer>) и автоупаковка. Wildcard <? extends T> и <? super T> в параметрах методов объясняет урок принцип PECS.

Неизменяемые коллекции

Неизменяемая (unmodifiable) коллекция — это коллекция, у которой методы add(), remove() и set() бросают UnsupportedOperationException. В современном Java их создают фабричными методами List.of(), Set.of(), Map.of() (Java 9) и List.copyOf() (Java 10). Различаются способы деталями: поддержкой null и связью с исходными данными.

Способ С версии Что можно менять null-элементы Связь с источником
Arrays.asList(...) 1.2 Только set(); add()/remove() — UnsupportedOperationException Да Представление массива: изменения видны в обе стороны
Collections.unmodifiableList(list) 1.2 Ничего Да Представление: изменения исходного списка видны
List.of(...), Set.of(...), Map.of(...) 9 Ничего Нет — NullPointerException (даже у contains(null)) Нет
List.copyOf(collection) 10 Ничего Нет — NullPointerException Нет, это копия
stream.toList() 16 Ничего Да Нет

Что выведет этот код?

import java.util.Arrays;
import java.util.List;

public class UnmodifiableExample {
    public static void main(String[] args) {
        List<String> fixed = Arrays.asList("a", "b");
        fixed.set(0, "z");
        System.out.println(fixed);

        try {
            fixed.add("c");
        } catch (UnsupportedOperationException e) {
            System.out.println("Arrays.asList: add() запрещён");
        }

        List<String> immutable = List.of("a", "b");
        try {
            immutable.set(0, "z");
        } catch (UnsupportedOperationException e) {
            System.out.println("List.of: set() запрещён");
        }
    }
}
[z, b]
Arrays.asList: add() запрещён
List.of: set() запрещён

Arrays.asList() возвращает список фиксированного размера, а не неизменяемый: заменить элемент можно, изменить размер — нет. Почему изменения такого списка попадают в исходный массив, показано в уроке Backed Collections. Если нужен обычный изменяемый список с начальными элементами, оберните фабрику: new ArrayList<>(List.of("a", "b")).

Потокобезопасные коллекции Java

Обычные ArrayList, HashSet и HashMap не потокобезопасны: при одновременной записи из нескольких потоков данные теряются или итерация падает с ConcurrentModificationException. Для многопоточного кода в Java есть три группы коллекций: унаследованные синхронизированные классы, обёртки Collections.synchronizedXxx() и классы пакета java.util.concurrent (Java 5).

Коллекция Как обеспечивается потокобезопасность Когда использовать
Vector, Hashtable Каждый метод synchronized Только в старом коде; замена — классы ниже
Collections.synchronizedList(), synchronizedMap() Обёртка с одной блокировкой на все методы; перебор нужно вручную заключать в synchronized (list) { ... } Редкий доступ из нескольких потоков к готовой коллекции
ConcurrentHashMap Блокировка отдельных корзин и CAS-операции; чтение без блокировок. null-ключи и значения запрещены Основной выбор для общей Map (кеши, счётчики)
CopyOnWriteArrayList, CopyOnWriteArraySet Каждая запись копирует внутренний массив; итератор работает со снимком и не бросает ConcurrentModificationException Много чтений, редкие изменения (списки слушателей)
ConcurrentLinkedQueue, ConcurrentLinkedDeque Неблокирующие алгоритмы на CAS Общая очередь без ожидания
ArrayBlockingQueue, LinkedBlockingQueue (интерфейс BlockingQueue) put() ждёт свободного места, take() — появления элемента Схема «производитель–потребитель»
ConcurrentSkipListMap, ConcurrentSkipListSet Список с пропусками (skip list) без глобальной блокировки Потокобезопасная замена TreeMap/TreeSet

Пример: два потока по 10 000 раз увеличивают общий счётчик в ConcurrentHashMap. Метод merge() выполняет чтение и запись атомарно, поэтому ни одно увеличение не теряется:

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ConcurrentCounter {
    public static void main(String[] args) throws InterruptedException {
        Map<String, Integer> counts = new ConcurrentHashMap<>();
        Runnable task = () -> {
            for (int i = 0; i < 10_000; i++) {
                counts.merge("clicks", 1, Integer::sum);
            }
        };

        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);
        t1.start();
        t2.start();
        t1.join();
        t2.join();

        System.out.println(counts.get("clicks")); // Вывод: 20000
    }
}

Если заменить ConcurrentHashMap на HashMap, результат станет непредсказуемым: обычно меньше 20000, потому что потоки перезаписывают изменения друг друга. Синхронизация потоков подробно разбирается в уроке Синхронизация потоков.

Уроки модуля по порядку

Модуль «Коллекции» идёт от общего к частному: сначала корневой интерфейс и структуры данных, затем каждая ветка иерархии — списки, множества, очереди, отображения, и в конце инструменты для работы с ними.

Урок Что в нём разбирается
Интерфейс Collection Методы, общие для всех коллекций
Структуры данных Список, стек, очередь, множество — без привязки к классам
List и ArrayList Методы List, устройство ArrayList, toArray()
Set, HashSet, LinkedHashSet Хеш-таблица, контракт equals() и hashCode()
SortedSet и TreeSet Отсортированные множества, subSet(), headSet()
Comparable и Comparator Как задать порядок сортировки объектов
NavigableSet ceiling(), floor(), higher(), lower()
Queue и Deque Методы очередей: offer(), poll(), peek(), push(), pop()
ArrayDeque Очередь и стек на массиве
LinkedList Двусвязный список, сравнение с ArrayList
PriorityQueue Очередь с приоритетами
Принцип PECS Wildcard extends и super в коллекциях
Iterator Перебор коллекций, ConcurrentModificationException
Map HashMap, TreeMap, LinkedHashMap, SortedMap, NavigableMap
Методы класса Collections sort(), binarySearch(), shuffle(), frequency()
Backed Collections Представления коллекций и Arrays.asList()
Legacy Classes Vector, Stack, Hashtable, Properties, Enumeration

Презентация с видео на Patreon →

Часто задаваемые вопросы

Чем Collection отличается от Collections?

Collection — интерфейс, корень ветки списков, множеств и очередей. Collections — служебный класс со статическими методами: sort(), reverse(), unmodifiableList(), synchronizedMap() и другими. Создать объект Collections нельзя, а Collection реализуют все списки, множества и очереди.

Почему Map не наследует интерфейс Collection?

Элемент Map — пара «ключ–значение», а методы Collection рассчитаны на одиночные элементы: add(E) для пары не имеет смысла. Поэтому Map — отдельная ветка иерархии, а с остальными коллекциями её связывают представления keySet(), values() и entrySet().

Можно ли хранить int в коллекции?

Напрямую нельзя: List<int> не скомпилируется, используется List<Integer> с автоупаковкой. Тут легко ошибиться: для List<Integer> вызов list.remove(1) удалит элемент с индексом 1, а не число 1. Чтобы удалить значение, пишут list.remove(Integer.valueOf(1)).

Какие коллекции использовать по умолчанию?

Для списка — ArrayList, для множества — HashSet, для отображения — HashMap, для очереди и стека — ArrayDeque вместо устаревшего Stack. Остальные реализации берут, когда нужен порядок вставки, сортировка или потокобезопасность. Сравнение классов — в разделе Какую реализацию выбрать.

Video Explanation

Prefer video format? Watch this lesson with examples and explanations.

Comments

unknown Jul 9, 2024
спасибо за отличный курс!

Please log in or register to have a possibility to add comment.