2010-03-10 69 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); 
    } 
}