Что делает Collections.unmodifiableSet() в Java?
Я вижу, что Collections.unmodifiableSet
возвращает немодифицируемое представление данного набора, но я не понимаю, почему мы не можем просто использовать модификатор final
, чтобы выполнить это.
В моем понимании final
объявляет константу: что-то, что нельзя изменить. Таким образом, если набор объявлен как константа, он не может быть изменен: ничто не может быть удалено из набора, и ничего не может быть добавлено.
Зачем нам нужно Collections.unmodifiableSet
?
Ответы
Ответ 1
final
объявляет ссылку на объект, которая не может быть изменена, например
private final Foo something = new Foo();
создает новый Foo
и помещает ссылку в something
. После этого невозможно изменить something
на другой экземпляр Foo
.
Это не предотвращает изменение внутреннего состояния объекта. Я все еще могу вызвать любые методы на Foo
, доступные для соответствующей области. Если один или несколько из этих методов изменяют внутреннее состояние этого объекта, то final
не будет препятствовать этому.
Таким образом, следующее:
private final Set<String> fixed = new HashSet<String>();
не создает Set
, который не может быть добавлен или иным образом изменен; это просто означает, что fixed
будет ссылаться только на этот экземпляр.
В отличие от этого:
private Set<String> fixed = Collections.unmodifiableSet( new HashSet<String>() );
создает экземпляр Set
, который будет бросать UnsupportedOperationException
, если попытаться вызвать fixed.add()
или fixed.remove()
, например - сам объект будет защищать его внутреннее состояние и не будет его изменять.
Для полноты:
private final Set<String> fixed = Collections.unmodifiableSet( new HashSet<String>() );
создает экземпляр Set
, который не позволяет изменять его внутреннее состояние, а также означает, что fixed
будет указывать только на экземпляр этого набора.
Причина, по которой final
может использоваться для создания констант примитивов, основана на том, что значение не может быть изменено. Помните, что fixed
выше было просто ссылкой - переменной, содержащей адрес, который нельзя изменить. Ну, для примитивов, например
private final int ANSWER = 42;
значение ANSWER
таково, что 42. Поскольку ANSWER
не может быть изменено, оно будет иметь когда-либо значение 42.
Пример, который размывает все строки:
private final String QUESTION = "The ultimate question";
В соответствии с приведенными выше правилами QUESTION
содержит адрес экземпляра String
, который представляет "Окончательный вопрос", и этот адрес не может быть изменен. Следует помнить, что String
сам неизменен - вы ничего не можете сделать с экземпляром String
, который его изменяет, и любыми операциями, которые в противном случае это сделали (например, replace
, substring
, и т.д.) возвращают ссылки на совершенно разные экземпляры String
.
Ответ 2
final
гарантирует, что ссылка на объект, который представляет переменная, не может быть изменена, он ничего не делает для экземпляра объекта и его изменчивости.
final Set s = new Set();
просто гарантирует, что вы не можете сделать s = new Set();
снова. Это не делает набор неизменяемым, если бы вы ничего не могли добавить к нему для начала. Поэтому, чтобы сделать это действительно ясно, final
влияет только на переменную reference, а не на объект, на который указывает ссылка.
Я могу сделать следующее:
final List<String> l = new ArrayList<String>();
l.add("hello");
l.add("world");
l.remove(0);
но я не могу этого сделать.
l = new ArrayList<String>();
снова из-за final
Я не могу изменить то, на что указывает переменная l.
вам нужно сделать одно из следующих трех действий, чтобы сделать поток контейнера коллекции безопасным.
java.util.Collections.syncronizedXXX();
или
java.util.Collections.unmodifiableXXX();
или
используйте один из соответствующих контейнеров из java.util.concurrency.* package
.
если у меня был объект Person
и сделал final Person p = new Person("me");
, это означает, что я не могу переназначить p
, чтобы указать на другой объект Person
. Я все еще могу сделать p.setFirstName("you");
Смущает ситуация, что
final int PI = 3.14;
final String greeting = "Hello World!";
выглядят как const
в С++, когда на самом деле объекты, на которые они указывают, неизменны/не поддаются изменению по своей природе. Контейнеры или объекты с мутаторными методами, которые могут изменять внутреннее состояние объекта, не являются const
только ссылкой для этих объектов final
и не могут быть переназначены на ссылку другой объект.
Ответ 3
final
не является (С++ - style) const
. В отличие от С++, Java не имеет методов const
или что-то в этом роде, а методы, которые могут изменить объект, могут быть вызваны с помощью ссылки final
.
Collections.unmodifiable*
- это оболочка, которая обеспечивает (только во время выполнения, а не во время компиляции) доступность только для чтения для соответствующей коллекции.
Ответ 4
Collections.unmodifiableSet(Set<? extends T>)
создаст оболочку на исходном наборе. Этот набор оберток не может быть изменен. но все же исходный набор может быть изменен.
Пример:
Set<String> actualSet=new HashSet<String>(); //Creating set
Добавление некоторых элементов
actualSet.add("aaa");
actualSet.add("bbb");
Печать добавленных элементов
System.out.println(actualSet); //[aaa, bbb]
Поместите actualSet
в немодифицируемый набор и назначьте его новой ссылке (wrapperSet
).
Set<String> wrapperSet=Collections.unmodifiableSet(orginalSet);
Распечатайте пакет wrapperSet. поэтому он будет иметь actualSet
Значения
System.out.println(wrapperSet); //[aaa, bbb]
попытается удалить/добавить один элемент на wrapperSet
.
wrapperSet.remove("aaa"); //UnSupportedOperationException
Добавьте еще один элемент в actualSet
actualSet .add("ccc");
Печать actualSet
и wrapperSet
. оба значения набора одинаковы. поэтому, если вы добавляете/удаляете какие-либо элементы при фактическом наборе, изменения также будут отражены в наборе оберток.
System.out.println(actualSet); //[aaa, ccc, bbb]
System.out.println(wrapperSet); // [aaa, ccc, bbb]
Применение:
Этот Collections.unmodifiableSet(Set<? extends T>)
используется для предотвращения модификации метода Get get для любого объекта. скажем,
public class Department{
private Set<User> users=new HashSet<User>();
public Set<User> getUsers(){
return Collections.unmodifiableSet(users);
}
}