Что делает 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); 
    }
}