2010-03-10 118 views
24

我可以看到Collections.unmodifiableSet返回给定集的不可修改视图,但我不明白为什么我们不能只使用final修改器来实现此目的。Collections.unmodifiableSet()在Java中做什么?

在我的理解中,final声明了一个常量:无法修改的东西。所以,如果一个集合被声明为一个常量,那么它就不能被修改:任何东西都不能被删除,也不能添加任何东西。我们需要Collections.unmodifiableSet

回答

51

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>()); 

创建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的一个实例进行任何修改,并且任何其他操作(如replacesubstring等)都会返回对完全不同的引用String的实例。

14

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); 

,但我不能这样做。因为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++中,而事实上它们指向的对象是不可改变/不可改变的天性。具有可以改变对象内部状态的增变器方法的容器或对象不是const只是参考那些对象是final并且不能被重新分配到参考另一个对象。

+1

好贴。总之,_reference_不能改变,但是对象的_contents_可以*。 – extraneon 2010-03-10 20:20:35

3

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

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

0

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); 

打印wrapperSet。所以它有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>)用于防止任何对象的设置的吸气剂的方法的修饰。让说

public class Department{ 

    private Set<User> users=new HashSet<User>(); 

    public Set<User> getUsers(){ 
     return Collections.unmodifiableSet(users); 
    } 
}