Вопрос

Я могу видеть это 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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top