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

115

Из документации Java 1.6 Collection Framework :

Коллекции, которые не поддерживают какие-либо операции модификации (например add, removeи clear), называются неизменяемыми . [...] Коллекции, которые дополнительно гарантируют, что никакие изменения в объекте Collection никогда не будут видны, называются неизменяемыми .

Второй критерий меня немного смущает. Учитывая, что первая коллекция не может быть изменена, и если исходная ссылка на коллекцию удалена, какие изменения упоминаются во второй строке? Относится ли это к изменениям в элементах, содержащихся в коллекции, то есть к состоянию элементов?

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

Чтобы коллекция была неизменной, как можно предоставить указанные дополнительные гарантии?

Bhaskar
источник
В целом (особенно в функциональных языках) неизменяемая (также известная как постоянная) коллекция может измениться в том смысле, что вы можете получить новое состояние этой коллекции, но в то же время старое состояние будет по-прежнему доступно по другим ссылкам. Например, newCol = oldCol.add("element")будет создана новая коллекция, которая является копией старой с еще одним элементом, и все ссылки на нее по- oldColпрежнему будут указывать на ту же неизменную старую коллекцию.
ffriend

Ответы:

154

Неизменяемые коллекции обычно представляют собой доступные только для чтения представления (оболочки) других коллекций. Вы не можете добавлять, удалять или очищать их, но основная коллекция может измениться.

Неизменяемые коллекции вообще нельзя изменить - они не обертывают другую коллекцию - у них есть свои собственные элементы.

Вот цитата из гуавы ImmutableList

В отличие от того Collections.unmodifiableList(java.util.List<? extends T>), что представляет собой представление отдельной коллекции, которая все еще может изменяться, экземпляр ImmutableListсодержит свои собственные личные данные и никогда не изменится.

Итак, в основном, чтобы получить неизменяемую коллекцию из изменяемой, вы должны скопировать ее элементы в новую коллекцию и запретить все операции.

Bozho
источник
@Bhaskar - см. Мой последний абзац.
Божо
1
если коллекция, которая заключена в другую неизменяемую коллекцию, не имеет других ссылок на нее, то является ли bahaviour неизменяемой коллекции в точности таким же, как неизменная коллекция?
Bhaskar
1
@Bozho, Если ссылка на резервную коллекцию не указана, а указана только неизменяемая ссылка на оболочку коллекции, то вы не можете ее изменить. Тогда почему вы говорите: «Вы не можете быть уверены»? Указывает ли он на сценарий, в котором какой-то поток или каким-то образом он изменяется, потому что резервная коллекция является изменяемой?
AKS
@AKS: когда коллекция обернута unmodifiableList, код, который получает только ссылку на этот список, не сможет его изменить, но любой код, который имел ссылку на исходный список и мог изменить его до создания оболочки, по-прежнему будет иметь возможность сделать это позже. Если код, создавший исходный список, знает, что произошло с каждой ссылкой, которая когда-либо существовала в нем, и знает, что ни одна из них не попадет в руки кода, который может изменить список, тогда он может знать, что список никогда не будет изменен. Однако, если ссылка была получена из внешнего кода ...
supercat
... нет возможности ни для unmodifiableList, ни для какого-либо кода, который его использует, узнать, может ли и как измениться обернутая коллекция.
supercat
86

Разница в том, что у вас не может быть ссылки на неизменяемую коллекцию, которая допускает изменения. Неизменяемые коллекции нельзя изменить с помощью этой ссылки , но какой-либо другой объект может указывать на те же данные, с помощью которых он может быть изменен.

например

List<String> strings = new ArrayList<String>();
List<String> unmodifiable = Collections.unmodifiableList(strings);
unmodifiable.add("New string"); // will fail at runtime
strings.add("Aha!"); // will succeed
System.out.println(unmodifiable);
Саймон Никерсон
источник
1
Какие различные причины, если таковые имеются, могут привести к изменению коллекции, которая не подлежит изменению, при условии, что исходная ссылка на коллекцию не используется для изменения базовой коллекции?
Bhaskar
21
Collection<String> c1 = new ArrayList<String>();
c1.add("foo");
Collection<String> c2 = Collections.unmodifiableList(c1);

c1является изменяемым (т.е. ни неизменным, ни неизменным ).
c2является неизменяемым : он не может быть изменен сам, но если позже я изменить c1то , что изменения будут видны в c2.

Это потому, что c2это просто оболочка, c1а не независимая копия. Guava предоставляет ImmutableListинтерфейс и некоторые реализации. Они работают, фактически создавая копию ввода (если только ввод не является неизменяемой коллекцией).

По поводу вашего второго вопроса:

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

Иоахим Зауэр
источник
2
@ Джон: нет, это не так. По крайней мере, не в соответствии с определением, приведенным в ОП.
Иоахим Зауэр
@JohnVint, правда? Я бы так не подумал, потому что, используя c1, я все еще могу добавлять в него элементы, и это добавление будет видно c2. Разве это не значит, что он не неизменен?
Bhaskar
Что ж, я думаю, если c1 может ускользнуть, он не будет неизменным, но неизменность также относится к содержимому в коллекции. Я больше имел в виду неизменяемую коллекцию java.util.Date
Джон Винт
1
@Vint: «если c1не избежать» - это нигде в спецификации не выделяется. На мой взгляд, если вы можете использовать коллекцию таким образом, чтобы сделать ее неизменяемой, то вы всегда должны считать ее неизменной.
Иоахим Зауэр
1
Позвольте процитировать определение: «Коллекции, которые дополнительно гарантируют …» (выделено мной). Collection.unmodifiableList() не может этого гарантировать, потому что не может гарантировать, что его аргумент не исчезнет. Гуава ImmutableList.of всегда создает неизменяемый объект List, даже если вы позволите его аргументам ускользнуть.
Иоахим Зауэр
17

Теперь в java 9 есть фабричные методы для неизменяемого списка, набора, карты и Map.Entry.

В Java SE 8 и более ранних версиях мы можем использовать служебные методы класса Collections, такие как unmodifiableXXX, для создания объектов Immutable Collection.

Однако эти методы Collections.unmodifiableXXX очень утомительны и многословны. Чтобы преодолеть эти недостатки, корпорация Oracle добавила несколько служебных методов в интерфейсы List, Set и Map.

Теперь в java 9: ​​- Интерфейсы List и Set имеют методы «of ()» для создания пустых или непустых объектов Immutable List или Set, как показано ниже:

Пример пустого списка

List immutableList = List.of();

Пример непустого списка

List immutableList = List.of("one","two","three");
Opster Elasticsearch Pro-Vijay
источник
2
А в Java 10 List.copyOfи Set.copyOfбыли добавлены, которые позволяют создавать неизменяемую копию списка / набора или возвращать данную коллекцию, если она уже не подлежит
Marcono1234
6

Я считаю, что суть здесь в том, что даже если коллекция не может быть изменена, это не гарантирует, что она не может измениться. Возьмем, к примеру, коллекцию, которая удаляет элементы, если они слишком старые. Неизменяемость просто означает, что объект, содержащий ссылку, не может ее изменить, а не то, что он не может измениться. Истинный пример этого -Collections.unmodifiableList метод. Он возвращает неизменяемое представление списка. Ссылка на список, переданная в этот метод, по-прежнему может быть изменена, поэтому список может быть изменен любым владельцем переданной ссылки. Это может привести к ConcurrentModificationExceptions и другим неприятным ситуациям.

Неизменяемые, означают, что коллекцию никак нельзя изменить.

Второй вопрос: неизменяемая коллекция не означает, что объекты, содержащиеся в коллекции, не изменятся, просто эта коллекция не изменится в количестве и составе объектов, которые она содержит. Другими словами, список литературы коллекции не изменится. Это не означает, что внутренняя часть объекта, на который имеется ссылка, не может измениться.

Джон Б.
источник
Хороший!! Поэтому, если я создам оболочку над Unmodifiable Collection и сделаю ссылку на изменяемую коллекцию частной, тогда я могу быть уверен, что она неизменна. право?
AKS
Если я понимаю ваш вопрос, то ответ отрицательный. Обертывание Unmodifiable не защищает его от передачи коллекции в unmodifiableListизмененную. Если вы хотите это сделать, используйте ImmutableList.
John B
0

Pure4J поддерживает то, что вам нужно, двумя способами.

Во-первых, он предоставляет @ImmutableValueаннотацию, так что вы можете аннотировать класс, чтобы сказать, что он неизменяемый. Существует плагин maven, который позволяет вам проверить, что ваш код действительно неизменен (использование finalи т. Д.).

Во-вторых, он предоставляет постоянные коллекции из Clojure (с добавленными универсальными шаблонами) и гарантирует, что элементы, добавленные в коллекции, неизменяемы. Производительность у них явно неплохая. Все коллекции неизменяемы, но для проверки реализуют интерфейсы коллекций Java (и дженерики). Мутация возвращает новые коллекции.

Отказ от ответственности: я разработчик этого

Роб Моффат
источник