Поэтому мы склонны думать, что видели все это, когда дело доходит до 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 .