我可以看到它Collections.unmodifiableSet
返回给定集合的不可修改视图,但我不明白为什么我们不能只使用final
修饰符来完成此操作。
在我的理解中,final
声明一个常量:不能修改的东西。所以,如果一个集合被声明为一个常量,那么它就不能被修改:不能从集合中删除任何东西,也不能添加任何东西。
为什么我们需要Collections.unmodifiableSet
?
我可以看到它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>() );
创建一个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 指向的内容。
您必须执行以下三件事之一才能使 Collection 容器线程安全。
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++ 中,实际上它们指向的对象本质上是不可变/不可修改的。具有可以改变对象内部状态的 mutator 方法的容器或对象const
不仅是对这些对象的引用final
,而且不能重新分配以引用另一个对象。
将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>)
用于防止修改 Set 的任何对象的 getter 方法。让我们说
public class Department{
private Set<User> users=new HashSet<User>();
public Set<User> getUsers(){
return Collections.unmodifiableSet(users);
}
}
final
不是 (C++ 风格) const
。与 C++ 不同,Java 没有-methods 或类似的东西,可以通过引用const
调用可以更改对象的方法。final
Collections.unmodifiable*
是一个包装器,它强制(仅在运行时,而不是在编译时)相关集合的只读性。
总结一下我们能做和不能做的:
准备:
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
private final Set words = Collections.unmodifiableSet(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