38

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

在我的理解中,final声明一个常量:不能修改的东西。所以,如果一个集合被声明为一个常量,那么它就不能被修改:不能从集合中删除任何东西,也不能添加任何东西。

为什么我们需要Collections.unmodifiableSet

4

5 回答 5

75

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它就会抛出,或者,例如 - 对象本身将保护它的内部状态并防止它被修改。UnsupportedOperationExceptionfixed.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改变它的实例做任何事情,否则任何会这样做的操作(例如replacesubstring等)都会返回对完全不同实例的引用String

于 2010-03-10T20:03:29.753 回答
18

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,而且不能重新分配以引用另一个对象。

于 2010-03-10T20:01:58.163 回答
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);

打印包装集。所以它会有actualSet价值观

System.out.println(wrapperSet);   //[aaa, bbb]

让我们尝试删除/添加一个元素wrapperSet

wrapperSet.remove("aaa");   //UnSupportedOperationException 

再添加一个元素actualSet

    actualSet .add("ccc");

打印actualSetwrapperSet. 两组值相同。因此,如果您在实际集上添加/删除任何元素,则更改也将反映在包装集上。

    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); 
    }
}
于 2014-03-20T14:39:44.913 回答
3

final不是 (C++ 风格) const。与 C++ 不同,Java 没有-methods 或类似的东西,可以通过引用const调用可以更改对象的方法。final

Collections.unmodifiable*是一个包装器,它强制(仅在运行时,而不是在编译时)相关集合的只读性。

于 2010-03-10T20:03:53.543 回答
1

总结一下我们能做和不能做的:

准备:

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
于 2018-07-13T09:19:58.583 回答