1伴奥、為什么需要不可變集合
- (1)保證線程安全:在并發(fā)程序中乍恐,使用Immutable既保證線程安全性默蚌,也大大增強了并發(fā)時的效率(跟并發(fā)鎖方式相比)。尤其當(dāng)一個對象是值對象時漱办,更應(yīng)該考慮采用Immutable方式这刷;
- (2)被不可信的類庫使用時會很安全;
- (3)如果一個對象不需要支持修改操作(mutation)娩井,將會節(jié)省空間和時間的開銷暇屋;經(jīng)過分析,所有不可變的集合實現(xiàn)都比可變集合更加有效地利用內(nèi)存撞牢;
- (4)可以當(dāng)作一個常量來對待率碾,并且這個對象在以后也不會被改變叔营。
將一個對象復(fù)制一份成immutable的屋彪,是一個防御性編程技術(shù)所宰。
2、JDK中提供的不可變集合:真的做到了不可變畜挥?
在JDK類庫中很多集合(List仔粥、Set、Map等)都可以調(diào)用Collections類提供的靜態(tài)方法unmodifiableXXX(…)來得到一個不可修改的視圖蟹但,例如:
// 下面的代碼利用Collections.unmodifiableList(list)得到一個不可修改的集合unmodifiableList
List list = new ArrayList();
list.add("wyp");
list.add("good");
List unmodifiableList = Collections.unmodifiableList(list);
System.out.println(unmodifiableList);//[wyp, good]
unmodifiableList.add("add");
當(dāng)unmodifiableList.add(“add”)時躯泰,運行代碼將會出現(xiàn)以下異常:
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.Collections$UnmodifiableCollection.add(Collections.java:1018)
at com.wyp.test.testFiles(test.java:152)
at com.wyp.test.main(test.java:160)
說明如果直接add是不可以的,符合不可變的定義华糖。
一切看起來很不錯麦向,因為調(diào)用unmodifiableList.add()會拋出一個java.lang.UnsupportedOperationException。但如果有用戶修改了list客叉,會發(fā)生什么情況诵竭?在上述代碼的下面加入以下代碼:
list.add("add");
System.out.println(unmodifiableList);
當(dāng)你再次打印unmodifiableList的時候,你會發(fā)現(xiàn)結(jié)果是[wyp, good, add]兼搏,多了一個"add"元素卵慰。unmodifiableList不是不可變的嗎?這顯然不是我們期望的佛呻。
說明:Collections.unmodifiableList(…)實現(xiàn)的不是真正的不可變集合裳朋,當(dāng)原始集合被修改后,不可變集合里面的元素也是跟著發(fā)生變化吓著。
利用JDK類庫中提供的unmodifiableXXX方法最少存在以下幾點不足:
- 笨拙:因為你每次都得寫那么多代碼鲤嫡;
- 不安全:如果沒有引用到原來的集合,這種情況之下才會返回唯一真正永恒不變的集合绑莺;
- 效率很低:返回的不可修改的集合數(shù)據(jù)結(jié)構(gòu)仍然具有可變集合的所有開銷泛范。
3、Guava提供的Immutable:真正的不可變集合
Guava類庫中提供的Immutable才是真正的不可修改的集合紊撕。
import com.google.common.collect.ImmutableList;
ImmutableList immutableList = ImmutableList.of("wyp", "good");
當(dāng)你往immutableList 中添加元素罢荡,也會拋出java.lang.UnsupportedOperationException異常;
-
修改原集合后对扶,immutable集合不變:
public void testImmutable(){
ArrayList<String> stringArrayList = Lists.newArrayList("wo","bu","ke","bian");
ImmutableList<String> immutableList = ImmutableList.copyOf(stringArrayList);
// 嘗試add: java.lang.UnsupportedOperationException
// immutableList.add("!!!");// 嘗試修改原集合:immutableList不變区赵,還是 [wo, bu, ke, bian] stringArrayList.add("!!!"); System.out.println(immutableList); }
閱讀源碼:Guava Immutable的實現(xiàn)原理
單步調(diào)試走起:
如果傳入的結(jié)合本身就是一個不可變集合,那么asList獲取視圖后返回浪南;如果不是笼才,則執(zhí)行construct方法;
上一步的checkNotNull方法將原集合轉(zhuǎn)換成了對象數(shù)組傳開了進來骡送,同時傳入了這個數(shù)組的長度昂羡,這里用case對長度做了判斷,以分別處理:
case 0:長度是0摔踱,那么of應(yīng)該是返回一個空的不可變集合虐先,而一般來說這個空的不可變集合是可以復(fù)用的;
case 1:長度是1派敷,那么直接用這個元素new一個對象實例蛹批;
-
default:這里有個地方?jīng)]看懂
if (length < elements.length) { elements = arraysCopyOf(elements, length); }
這里是由上一步而來的:
所以這里的length就是elements的length,那么這個if又有何意義篮愉?不明白...反正是直接執(zhí)行
return new RegularImmutableList<E>(elements);
了腐芍,接著往下看:
這是一個內(nèi)部類,直借截取了一部分试躏,里面有一個copyIntoArray方法
經(jīng)過一系列調(diào)用猪勇,得到了一個array
最后一步一步返回。