W3docs

Интерфейс Collection в Java

Корневой интерфейс Collection в Java и контракт, который наследуют список, множество и очередь.

java.util.Collection<E> — это корень той части фреймворка, которая хранит отдельные элементы: каждый List, Set, Queue и Deque его реализует (единственное семейство, которое не делает этого, — Map, чьи элементы представляют собой записи, а не одиночные значения). Всё, что можно сделать «вне зависимости от того, какая это группа» — добавить, удалить, проверить contains, выполнить итерацию, посчитать элементы, преобразовать в массив, открыть поток — объявлено здесь. Эта глава описывает контракт: методы, на которые можно рассчитывать в любой коллекции, те немногие, что могут бросать исключения, и модель итерации, которую наследует каждая реализация.

Иерархия с первого взгляда

Iterable<E>
  └── Collection<E>
        ├── List<E>           — ordered, indexed, duplicates allowed
        ├── Set<E>            — no duplicates
        │     └── SortedSet<E> → NavigableSet<E>
        └── Queue<E>          — "next in line"
              └── Deque<E>    — double-ended queue

Collection расширяет Iterable<E>, именно поэтому каждая коллекция работает с циклом for-each. Две специализации CollectionSet и Queue — уточняют контракт; Map является самостоятельным корнем.

Основные методы, присутствующие в каждой коллекции

Ниже приведён полный набор методов экземпляра интерфейса Collection, сгруппированных по назначению. Лучше запомнить категории, а не весь список — как только вы узнаете, что существует метод removeIf, вы сможете его найти.

Размер и пустота

  • int size() — количество элементов.
  • boolean isEmpty()size() == 0, но зачастую работает быстрее.

Добавление

  • boolean add(E e) — добавляет один элемент. Возвращает true, если коллекция изменилась. (Для Set возвращает false при попытке добавить дубликат.)
  • boolean addAll(Collection<? extends E> c) — добавляет каждый элемент из c.

Удаление

  • boolean remove(Object o) — удаляет одно вхождение o.
  • boolean removeAll(Collection<?> c) — удаляет каждый элемент, присутствующий в c.
  • boolean retainAll(Collection<?> c) — оставляет только элементы из c (пересечение).
  • boolean removeIf(Predicate<? super E> filter) — удаляет каждый элемент, соответствующий предикату. Наиболее чистый способ фильтрации на месте.
  • void clear() — очищает коллекцию.

Запросы

  • boolean contains(Object o) — проверка вхождения.
  • boolean containsAll(Collection<?> c) — проверка подмножества.

Итерация и групповые представления

  • Iterator<E> iterator() — базовый итератор; используется циклом for-each.
  • Stream<E> stream() / parallelStream() — открывает Stream по элементам.
  • void forEach(Consumer<? super E> action) — унаследован от Iterable. Функциональная форма итерации.

Преобразование в массив

  • Object[] toArray()
  • <T> T[] toArray(T[] a) и более новый <T> T[] toArray(IntFunction<T[]> generator) (Java 11+) — типизированный массив.

Это весь интерфейс. Каждый список, множество и очередь, с которыми вы будете работать в этой части, — лишь разные реализации этих методов плюс несколько собственных.

Равенство и equals / hashCode

Collection.equals(Object) не определён на корневом уровне — каждый подинтерфейс сам задаёт, что для него означает равенство. List требует одинакового порядка и одинаковых элементов; Set требует одинаковых элементов вне зависимости от порядка; Queue вообще не определяет equals (очередь на основе LinkedList и очередь на основе ArrayDeque с одинаковым содержимым не будут равны, так как сравнение возвращается к сравнению по идентичности). Не сравнивайте коллекции из разных семейств, ожидая симметрии.

Элементы, хранящиеся в Collection, должны иметь правильно реализованные equals / hashCode, если вы хотите, чтобы методы contains, remove и (для коллекций на основе хэш-таблиц) поиск работали корректно. Контракт этих методов описан в главе equals и hashCode — это необходимое условие для использования Set и Map с вашими классами.

Необязательные операции

Некоторые коллекции являются немодифицируемыми — List.of(1,2,3), Collections.unmodifiableList(list), представления, возвращаемые Map.keySet() в определённых реализациях, и т. д. Они по-прежнему реализуют Collection, но вызов методов add, remove, clear или любого мутирующего метода бросает UnsupportedOperationException. В Javadoc они называются «необязательными операциями». Это наиболее близкое в Java к отказу от части интерфейса во время выполнения; цена такого подхода — компилятор не может обнаружить ошибку, и вы узнаёте о ней только при первом исключении.

Безопасное правило: если вы не создавали коллекцию сами, считайте её потенциально немодифицируемой. Если вам нужна изменяемая копия, сначала выполните new ArrayList<>(received).

Итерация: три формы, один базовый механизм

Каждая коллекция поддерживает три стиля итерации, и все три в итоге вызывают iterator():

Collection<String> names = List.of("Ada", "Linus", "Grace");

// 1. for-each — the everyday form
for (String n : names) System.out.println(n);

// 2. forEach with a lambda — declarative
names.forEach(System.out::println);

// 3. Iterator — when you need to remove during iteration
Iterator<String> it = names.iterator();
while (it.hasNext()) {
  String s = it.next();
  if (s.startsWith("L")) it.remove();   // safe; for-each can't do this
}

Почему формы 1 и 3 по-прежнему обе актуальны: цикл for-each не может изменять базовую коллекцию, не бросив ConcurrentModificationException. Когда нужно удалять элементы во время итерации, вы прибегаете к явному Iterator. Глава Итераторы далее в этой части подробно описывает протокол работы с ними.

Алгебра множеств с помощью групповых операций

Групповые операции превращают коллекцию в вычислитель алгебры множеств (независимо от того, является ли она Set — они работают и с List):

Collection<Integer> a = new ArrayList<>(List.of(1, 2, 3, 4));
Collection<Integer> b = List.of(3, 4, 5);

a.addAll(b);                 // union (multiset)
a.retainAll(List.of(3, 4));  // intersection
a.removeAll(List.of(3));     // difference

Это безопасный, независимый от сторонних библиотек способ выразить «оставить только элементы, присутствующие также в b», без написания цикла. Операции изменяют получателя — если вам нужен неизменяемый результат, сначала скопируйте.

Практический пример: все методы рядом

Программа ниже применяет каждую категорию методов Collection к одному ArrayList, чтобы вы могли видеть их в одном месте и наблюдать, как работает контракт.

java— editable, runs on the server

Стоит обратить внимание на два момента в выводе программы:

  1. remove("red") удалил только первое вхождение — таков контракт Collection. Чтобы удалить все совпадения, используйте removeIf (далее в примере он удаляет каждое слово длиннее четырёх символов).
  2. UnsupportedOperationException от frozen.add("d") — это правило «необязательных операций» в действии. frozen реализует Collection, поэтому вызов компилируется. Реализация решила не поддерживать данную операцию, и вы узнаёте об этом во время выполнения.

Что дальше

Collection — абстрактный контракт. Первое конкретное уточнение, с которым вы познакомитесь, — то, которое добавляет порядок и индексациюинтерфейс List. Именно там появляются доступ по индексу, подсписки и операции, сохраняющие порядок.

Практика

Практика
У вас есть `Collection<String>`, и вы хотите удалить каждый элемент, длина которого больше четырёх символов. Какой подход является идиоматичным и реализуется в одном выражении?
У вас есть `Collection<String>`, и вы хотите удалить каждый элемент, длина которого больше четырёх символов. Какой подход является идиоматичным и реализуется в одном выражении?
Was this page helpful?