Collections.unmodifiableSet()在Java中做了什么?

Rom*_*man 31 java final unmodifiable

我可以看到Collections.unmodifiableSet返回给定集合的不可修改的视图但我不明白为什么我们不能只使用final修饰符来完成此操作.

在我的理解中,final声明一个常量:无法修改的东西.因此,如果将一个集合声明为常量,则无法修改它:无法从集合中删除任何内容,也无法添加任何内容.

我们为什么需要Collections.unmodifiableSet

Rob*_*Rob 65

final 声明一个无法修改的对象引用,例如

private final Foo something = new Foo();
Run Code Online (Sandbox Code Playgroud)

创建一个新的Foo并将引用放入something.此后,不可能something改为指向不同的实例Foo.

这并不能防止对象的内部状态的修改.我仍然可以调用Foo相关范围可访问的任何方法.如果这些方法中的一个或多个修改了该对象的内部状态,那么final就不会阻止它.

因此,以下内容:

private final Set<String> fixed = new HashSet<String>();
Run Code Online (Sandbox Code Playgroud)

没有创建一个Set不能被添加到或以其他方式改变; 它只意味着fixed只会引用该实例.

相比之下,做:

private Set<String> fixed = Collections.unmodifiableSet( new HashSet<String>() );
Run Code Online (Sandbox Code Playgroud)

创建一个实例,如果一个人试图调用Set它将抛出UnsupportedOperationException,fixed.add()或者fixed.remove()例如 - 对象本身将保护其内部状态并防止它被修改.

为了完整起见:

private final Set<String> fixed = Collections.unmodifiableSet( new HashSet<String>() );
Run Code Online (Sandbox Code Playgroud)

创建一个Set不允许更改其内部状态的实例,也意味着fixed只会指向该集的实例.

final可用于创建基元常量的原因是基于无法更改值的事实.请记住,fixed上面只是一个引用 - 一个包含无法更改的地址的变量.好吧,对于原始人,例如

private final int ANSWER = 42;
Run Code Online (Sandbox Code Playgroud)

ANSWER是42.因为ANSWER不能改变,它只会有42的值.

模糊所有线条的一个例子是:

private final String QUESTION = "The ultimate question";
Run Code Online (Sandbox Code Playgroud)

根据上述规则,QUESTION包含实例的地址String代表"最终问题",并且该地址不能更改.这里要记住的一点是,String本身是不可变的-你不能做任何事情的一个实例,String从而改变它,任何操作,否则这样做(如replace,substring等)返回引用的完全不同的情况String.


小智 15

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);
Run Code Online (Sandbox Code Playgroud)

但我不能这样做.

l = new ArrayList<String>();
Run Code Online (Sandbox Code Playgroud)

再次因为final我无法修改l指向的变量.

您必须执行以下三项操作之一才能使Collection容器线程安全.

java.util.Collections.syncronizedXXX();
Run Code Online (Sandbox Code Playgroud)

要么

java.util.Collections.unmodifiableXXX();
Run Code Online (Sandbox Code Playgroud)

或者使用其中一个合适的容器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!";
Run Code Online (Sandbox Code Playgroud)

看起来像const在C++中,实际上它们指向的对象本质上是不可变的/不可修改的.具有可以改变对象内部状态的mutator方法的容器或对象const不仅仅是对这些对象的引用,final而且不能重新分配以引用另一个对象.