Что делает Collections.unmodifiableSet() в Java?
-
19-09-2019 - |
Вопрос
Я могу видеть это Collections.unmodifiableSet
возвращает неизменяемый вид данного набора, но я не понимаю, почему мы не можем просто использовать final
модификатор для достижения этой цели.
В моем понимании, final
объявляет константу:что-то, что не может быть изменено.Итак, если набор объявлен как константа, то он не может быть изменен:ничто не может быть удалено из набора и ничего не может быть добавлено.
Зачем нам нужно Collections.unmodifiableSet
?
Решение
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>() );
создает экземпляр a Set
который бросит UnsupportedOperationException
если кто-то попытается вызвать fixed.add()
или fixed.remove()
, например - сам объект защитит свое внутреннее состояние и предотвратит его изменение.
Для полноты картины:
private final Set<String> fixed = Collections.unmodifiableSet( new HashSet<String>() );
создает экземпляр a 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
.
Другие советы
final
только гарантирует, что ссылка для объекта, который представляет переменная, он не может быть изменен, он ничего не делает для экземпляра объекта и его изменчивости.
final Set s = new Set();
просто гарантирует, что вы не можете сделать s = new Set();
снова.Это не делает набор неизменяемым, если бы это было так, вы с самого начала ничего не смогли бы к нему добавить.Итак, чтобы сделать это действительно ясным, final
влияет только на переменную ссылка не тот объект, на который указывает ссылка.
Я могу сделать следующее:
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
в C ++, когда на самом деле объекты, на которые они указывают, являются неизменяемыми / неизменяемыми по своей природе.Контейнеры или объекты с методами-мутаторами, которые могут изменять внутреннее состояние объекта, не являются const
просто тот ссылка к этим объектам относятся final
и не может быть переназначен на ссылка другой объект.
final
не является (в стиле C ++) const
.В отличие от C ++, Java не имеет const
-методы или что-нибудь подобное, и методы, которые могут изменять объект, могут быть вызваны через final
ссылка.
Collections.unmodifiable*
это оболочка, которая обеспечивает (только во время выполнения, а не во время компиляции) доступность только для чтения для соответствующей коллекции.
В 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);
Распечатайте набор оберток.так что это будет иметь 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>)
используется для предотвращения модификации метода getter Set для любого объекта.позвольте сказать
public class Department{
private Set<User> users=new HashSet<User>();
public Set<User> getUsers(){
return Collections.unmodifiableSet(users);
}
}
Подведите итог тому, что мы можем сделать и чего не можем:
Подготовка:
private Set<String> words = new HashSet<>(Arrays.asList("existing word"));
Окончательный по ссылке
private final Set<String> words = new HashSet<>();
может:
words.add("new word");
не могу:
words = new HashSet<>(); //compilation error
Окончательный по ссылке и не поддающийся изменению по коллекции.
частный конечный набор слов = Коллекции.Неизменяемый набор (words);
может:
String word = words.iterator().next();
не могу:
words = new HashSet<>(); // compilation error
words.add("new word"); // runtime error UnsupportedOperationException
Окончательный по ссылке и неизменяемый по коллекции, но взаимный как объект коллекции.
Но если у вас есть коллекция с взаимный объекты вы можете ИЗМЕНИТЬ внутреннее состояние этого объекта.
class A {
public int a; //mutable field. I can change it after initialization
public A(int a) {this.a = a;}
}
private final Set<A> set = Collections.unmodifiableSet(Arrays.asList(new A(25)));
Все еще не могу
set = new HashSet<>(); // compilation error
set.add(new A(777)); // runtime error UnsupportedOperationException
Но может
A custom = words.iterator().next(); //here custom.a = 25;
custom.a = 777; //here first element of **final, unmodifible** collection
//was changed from 25 to 777