Интерфейс Collection в Java: методы и контракт - Вопросы

Всего: 6 вопросов

1. 

Что такое интерфейс Collection в Java и какие методы он объявляет?

java.util.Collection<E> — корневой интерфейс ветки списков, множеств и очередей в Collection Framework. Он расширяет Iterable<E> и объявляет операции, общие для List, Set и Queue. Map интерфейс Collection не наследует.

Методы делятся на три группы:

  • изменение коллекции: add, remove, addAll, removeAll, retainAll, removeIf, clear;
  • проверки: contains, containsAll, size, isEmpty;
  • перебор и преобразование: iterator, toArray, stream, parallelStream, spliterator (и forEach из Iterable).

Кроме того, в Collection заново объявлены equals() и hashCode(). Методов с индексом (например, get(int)) в Collection нет: у множеств и очередей нет позиций элементов, индексы есть только в List.

2. 

Что возвращают методы add(), addAll(), remove(), removeAll(), retainAll() и removeIf() интерфейса Collection?

Все они возвращают boolean: true, если коллекция изменилась, и false, если осталась прежней. Если реализация отказывается выполнить операцию по другой причине, она бросает исключение, а не возвращает false.

  • add(e) — false, например, у Set, где такой элемент уже есть.
  • addAll(c) — true, если добавлен хотя бы один элемент: для HashSet, где часть элементов уже была, результат тоже true.
  • remove(o) — удаляет только первое вхождение, равное o по equals().
  • removeAll(c) — удаляет все элементы, которые есть в c, включая повторы; отсутствующие элементы c не мешают вернуть true.
  • retainAll(c) — оставляет только элементы из c; true, если что-то удалено.
  • removeIf(filter) — удаляет элементы, для которых условие истинно; true, если удалён хотя бы один.
3. 

Что такое необязательные операции Collection и почему List.of("a", "b").remove("z") бросает исключение, а Arrays.asList("a", "b").remove("z") возвращает false?

Методы, которые меняют коллекцию (add, addAll, remove, removeAll, retainAll, removeIf, clear), в документации помечены как необязательные (optional operations). Реализация вправе их не поддерживать и бросать UnsupportedOperationException. Так устроены List.of(), Collections.unmodifiableCollection(), а для операций, меняющих размер, — Arrays.asList().

Если вызов ничего не изменил бы, необязательная операция может бросить исключение, но не обязана. Коллекции из List.of() отказывают сразу, не глядя на аргумент. Список Arrays.asList() сначала ищет элемент и бросает исключение, только когда действительно нужно удалить: remove("z") вернёт false, а remove("a") бросит UnsupportedOperationException.

Вывод для практики: по объявлению Collection<String> c нельзя понять, поддерживает ли объект add(). Если метод получил коллекцию извне и должен её менять, надёжнее работать с копией new ArrayList<>(c).

4. 

Какие есть способы получить массив из коллекции через toArray() и копирует ли он элементы?

Вариантов три:

  • Object[] toArray() — возвращает Object[]; привести его к String[] нельзя (ClassCastException), даже если внутри только строки;
  • <T> T[] toArray(T[] a) — массив нужного типа, обычно toArray(new String[0]);
  • toArray(IntFunction<T[]> generator) (Java 11) — массив по ссылке на конструктор: toArray(String[]::new).

Если тип элементов не подходит под тип массива, два последних варианта бросают ArrayStoreException.

Массив всегда новый и не связан с коллекцией: присваивание array[0] = ... коллекцию не изменит. Но сами объекты не копируются — в массив попадают ссылки на те же объекты. Если в списке лежит new StringBuilder("a"), то после ((StringBuilder) list.toArray()[0]).append("!") список выведется как [a!].

5. 

Когда две коллекции в Java равны по equals()?

Сам Collection правило равенства не задаёт — его уточняют подинтерфейсы:

  • два List равны, если содержат равные элементы в том же порядке, независимо от класса: ArrayList с [1, 2, 3] равен такому же LinkedList;
  • два Set равны, если содержат одни и те же элементы: HashSet равен TreeSet с теми же элементами;
  • список никогда не равен множеству.

ArrayDeque, PriorityQueue и коллекция map.values() не являются ни List, ни Set и не переопределяют equals(), поэтому сравниваются по ссылке: две очереди с одинаковыми элементами не равны.

Хеш-код тоже зависит от подинтерфейса: у List он учитывает порядок элементов, у Set равен сумме хеш-кодов элементов. Сами элементы методы contains(), remove(), removeAll() ищут через equals(), поэтому в своих классах его нужно переопределять.

6. 

Как менялся интерфейс Collection по версиям Java и зачем в нём default-методы?

  • Java 1.2 — интерфейс появился вместе с Collection Framework: add, remove, contains, size, iterator, toArray и другие.
  • Java 5 — стал обобщённым (Collection<E>) и получил родителя Iterable, отсюда цикл for-each.
  • Java 8 — default-методы removeIf(), stream(), parallelStream(), spliterator(); forEach() в Iterable.
  • Java 11 — default-метод toArray(IntFunction<T[]>).
  • Java 21 — подинтерфейс SequencedCollection.

Default-методы имеют реализацию прямо в интерфейсе. Поэтому новые методы добавились в Collection, не сломав старые классы, которые его реализуют: им не пришлось ничего дописывать.

Страница 1 из 1