Статьи

Причуды API Коллекций Java

Поэтому мы склонны думать, что видели все это, когда дело доходит до API коллекций Java. Мы знаем, как обходить списки , множества , карты , итераторы , итераторы . Мы готовы к улучшениям API коллекций Java 8 .

Но потом, время от времени, мы наталкиваемся на одну из этих странных причуд, которые происходят из глубин JDK и его долгой истории обратной совместимости. Давайте посмотрим на неизменяемые коллекции

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

Является ли коллекция модифицируемой или нет, не отражается API коллекций. Там

не является неизменным базовым типом List , Set или Collection , который могут расширяться изменяемыми подтипами. Итак, следующий API не существует в JDK:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
// Immutable part of the Collection API
public interface Collection {
  boolean  contains(Object o);
  boolean  containsAll(Collection<?> c);
  boolean  isEmpty();
  int      size();
  Object[] toArray();
  <T> T[]  toArray(T[] array);
}
 
// Mutable part of the Collection API
public interface MutableCollection
extends Collection {
  boolean  add(E e);
  boolean  addAll(Collection<? extends E> c);
  void     clear();
  boolean  remove(Object o);
  boolean  removeAll(Collection<?> c);
  boolean  retainAll(Collection<?> c);
}

Теперь, вероятно, есть причины, почему вещи не были реализованы таким образом в первые дни Java. Скорее всего, изменчивость не рассматривалась как особенность, достойная занимать свой собственный тип в иерархии типов. Итак, появился вспомогательный класс Collections с полезными методами, такими как unmodifiableList() , unmodifiableSet() , unmodifiableCollection() и другими. Но будьте осторожны при использовании неизменяемых коллекций! В Javadoc упоминается очень странная вещь : возвращаемая коллекция не передает hashCode и операции equals в вспомогательную коллекцию, но использует методы Object и hashCode. Это необходимо для сохранения контрактов этих операций в случае, если резервная коллекция представляет собой набор или список. «Сохранить контракты этих операций». Это довольно расплывчато. В чем причина этого? Хорошее объяснение дано в этом ответе переполнения стека здесь :

UnmodifiableList — это UnmodifiableCollection, но это не так в обратном порядке — UnmodifiableCollection, которая оборачивает список, не является UnmodifiableList. Таким образом, если вы сравниваете UnmodifiableCollection, которая упаковывает List a, с UnmodifiableList, который упаковывает один и тот же List a, эти две оболочки не должны быть равными. Если бы вы только что прошли через завернутый список, они были бы равны. Хотя эти рассуждения верны, последствия могут быть довольно неожиданными.

Нижняя линия

Суть в том, что вы не можете полагаться на Collection.equals() . Хотя List.equals() и Set.equals() четко определены, не доверяйте Collection.equals() . Это может не вести себя осмысленно. Помните об этом, принимая коллекцию в сигнатуре метода:

1
2
3
4
5
public class MyClass {
  public void doStuff(Collection<?> collection) {
    // Don't rely on collection.equals() here!
  }
}

Ссылка: Java Collections API Причуды от нашего партнера по JCG Лукаса Эдера из блога JAVA, SQL и JOOQ .