Immutable(不可变)集合
Immutable(不可變)集合
不可變集合,顧名思義就是說集合是不可被修改的。集合的數(shù)據(jù)項(xiàng)是在創(chuàng)建的時(shí)候提供,并且在整個(gè)生命周期中都不可改變。
為什么要用immutable對象?immutable對象有以下的優(yōu)點(diǎn):
1.對不可靠的客戶代碼庫來說,它使用安全,可以在未受信任的類庫中安全的使用這些對象
2.線程安全的:immutable對象在多線程下安全,沒有競態(tài)條件
3.不需要支持可變性, 可以盡量節(jié)省空間和時(shí)間的開銷. 所有的不可變集合實(shí)現(xiàn)都比可變集合更加有效的利用內(nèi)存 (analysis)
4.可以被使用為一個(gè)常量,并且期望在未來也是保持不變的
immutable對象可以很自然地用作常量,因?yàn)樗鼈兲焐褪遣豢勺兊膶τ趇mmutable對象的運(yùn)用來說,它是一個(gè)很好的防御編程(defensive programming)的技術(shù)實(shí)踐。
JDK中實(shí)現(xiàn)immutable集合
在JDK中提供了Collections.unmodifiableXXX系列方法來實(shí)現(xiàn)不可變集合, 但是存在一些問題,下面我們先看一個(gè)具體實(shí)例:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
public class ImmutableTest {
@Test
public void testJDKImmutable(){
List<String> list=new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
System.out.println(list);
List<String> unmodifiableList=Collections.unmodifiableList(list);
System.out.println(unmodifiableList);
List<String> unmodifiableList1=Collections.unmodifiableList(Arrays.asList("a","b","c"));
System.out.println(unmodifiableList1);
String temp=unmodifiableList.get(1);
System.out.println("unmodifiableList [0]:"+temp);
list.add("baby");
System.out.println("list add a item after list:"+list);
System.out.println("list add a item after unmodifiableList:"+unmodifiableList);
unmodifiableList1.add("bb");
System.out.println("unmodifiableList add a item after list:"+unmodifiableList1);
unmodifiableList.add("cc");
System.out.println("unmodifiableList add a item after list:"+unmodifiableList);
}
}
輸出:
[a, b, c] [a, b, c] [a, b, c] unmodifiableList [0]:b list add a item after list:[a, b, c, baby] list add a item after unmodifiableList1:[a, b, c, baby]
說明:Collections.unmodifiableList實(shí)現(xiàn)的不是真正的不可變集合,當(dāng)原始集合修改后,不可變集合也發(fā)生變化。不可變集合不可以修改集合數(shù)據(jù),當(dāng)強(qiáng)制修改時(shí)會(huì)報(bào)錯(cuò),實(shí)例中的最后兩個(gè)add會(huì)直接拋出不可修改的錯(cuò)誤。
總結(jié)一下JDK的Collections.unmodifiableXXX方法實(shí)現(xiàn)不可變集合的一些問題:
1.它用起來笨拙繁瑣你不得不在每個(gè)防御性編程拷貝的地方用這個(gè)方法
2.它不安全:如果有對象reference原始的被封裝的集合類,這些方法返回的集合也就不是正真的不可改變。
3.效率低:因?yàn)樗祷氐臄?shù)據(jù)結(jié)構(gòu)本質(zhì)仍舊是原來的集合類,所以它的操作開銷,包括并發(fā)下修改檢查,hash table里的額外數(shù)據(jù)空間都和原來的集合是一樣的。
Guava的immutable集合
Guava提供了對JDK里標(biāo)準(zhǔn)集合類里的immutable版本的簡單方便的實(shí)現(xiàn),以及Guava自己的一些專門集合類的immutable實(shí)現(xiàn)。當(dāng)你不希望修改一個(gè)集合類,或者想做一個(gè)常量集合類的時(shí)候,使用immutable集合類就是一個(gè)最佳的編程實(shí)踐。
注意:每個(gè)Guava immutable集合類的實(shí)現(xiàn)都拒絕null值。我們做過對Google內(nèi)部代碼的全面的調(diào)查,并且發(fā)現(xiàn)只有5%的情況下集合類允許null值,而95%的情況下都拒絕null值。萬一你真的需要能接受null值的集合類,你可以考慮用Collections.unmodifiableXXX。
Immutable集合使用方法:
一個(gè)immutable集合可以有以下幾種方式來創(chuàng)建:
1.用copyOf方法, 譬如, ImmutableSet.copyOf(set)
2.使用of方法,譬如,ImmutableSet.of("a", "b", "c")或者ImmutableMap.of("a", 1, "b", 2)
3.使用Builder類
實(shí)例:
@Test
public void testGuavaImmutable(){
List<String> list=new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
System.out.println("list:"+list);
ImmutableList<String> imlist=ImmutableList.copyOf(list);
System.out.println("imlist:"+imlist);
ImmutableList<String> imOflist=ImmutableList.of("peida","jerry","harry");
System.out.println("imOflist:"+imOflist);
ImmutableSortedSet<String> imSortList=ImmutableSortedSet.of("a", "b", "c", "a", "d", "b");
System.out.println("imSortList:"+imSortList);
list.add("baby");
System.out.println("list add a item after list:"+list);
System.out.println("list add a item after imlist:"+imlist);
ImmutableSet<Color> imColorSet =
ImmutableSet.<Color>builder()
.add(new Color(0, 255, 255))
.add(new Color(0, 191, 255))
.build();
System.out.println("imColorSet:"+imColorSet);
}
輸出:
list:[a, b, c] imlist:[a, b, c] imOflist:[peida, jerry, harry] imSortList:[a, b, c, d] list add a item after list:[a, b, c, baby] list add a item after imlist:[a, b, c] imColorSet:[java.awt.Color[r=0,g=255,b=255], java.awt.Color[r=0,g=191,b=255]]
對于排序的集合來說有例外,因?yàn)樵氐捻樞蛟跇?gòu)建集合的時(shí)候就被固定下來了。譬如,ImmutableSet.of("a", "b", "c", "a", "d", "b"),對于這個(gè)集合的遍歷順序來說就是"a", "b", "c", "d"。
更智能的copyOf
copyOf方法比你想象的要智能,ImmutableXXX.copyOf會(huì)在合適的情況下避免拷貝元素的操作-先忽略具體的細(xì)節(jié),但是它的實(shí)現(xiàn)一般都是很“智能”的。譬如:
@Test
public void testCotyOf(){
ImmutableSet<String> imSet=ImmutableSet.of("peida","jerry","harry","lisa");
System.out.println("imSet:"+imSet);
ImmutableList<String> imlist=ImmutableList.copyOf(imSet);
System.out.println("imlist:"+imlist);
ImmutableSortedSet<String> imSortSet=ImmutableSortedSet.copyOf(imSet);
System.out.println("imSortSet:"+imSortSet);
List<String> list=new ArrayList<String>();
for(int i=0;i<20;i++){
list.add(i+"x");
}
System.out.println("list:"+list);
ImmutableList<String> imInfolist=ImmutableList.copyOf(list.subList(2, 18));
System.out.println("imInfolist:"+imInfolist);
int imInfolistSize=imInfolist.size();
System.out.println("imInfolistSize:"+imInfolistSize);
ImmutableSet<String> imInfoSet=ImmutableSet.copyOf(imInfolist.subList(2, imInfolistSize-3));
System.out.println("imInfoSet:"+imInfoSet);
}
輸出:
imSet:[peida, jerry, harry, lisa] imlist:[peida, jerry, harry, lisa] imSortSet:[harry, jerry, lisa, peida] list:[0x, 1x, 2x, 3x, 4x, 5x, 6x, 7x, 8x, 9x, 10x, 11x, 12x, 13x, 14x, 15x, 16x, 17x, 18x, 19x] imInfolist:[2x, 3x, 4x, 5x, 6x, 7x, 8x, 9x, 10x, 11x, 12x, 13x, 14x, 15x, 16x, 17x] imInfolistSize:16 imInfoSet:[4x, 5x, 6x, 7x, 8x, 9x, 10x, 11x, 12x, 13x, 14x]
在這段代碼中,ImmutableList.copyOf(imSet)會(huì)智能地返回時(shí)間復(fù)雜度為常數(shù)的ImmutableSet的imSet.asList()。
一般來說,ImmutableXXX.copyOf(ImmutableCollection)會(huì)避免線性復(fù)雜度的拷貝操作。如在以下情況:
這個(gè)操作有可能就利用了被封裝數(shù)據(jù)結(jié)構(gòu)的常數(shù)復(fù)雜度的操作。但例如ImmutableSet.copyOf(list)不能在常數(shù)復(fù)雜度下實(shí)現(xiàn)。
這樣不會(huì)導(dǎo)致內(nèi)存泄漏-例如,你有個(gè)ImmutableList<String> imInfolist,然后你顯式操作ImmutableList.copyOf(imInfolist.subList(0, 10))。這樣的操作可以避免意外持有不再需要的在hugeList里元素的reference。
它不會(huì)改變集合的語意-像ImmutableSet.copyOf(myImmutableSortedSet)這樣的顯式拷貝操作,因?yàn)樵贗mmutableSet里的hashCode()和equals()的含義和基于comparator的ImmutableSortedSet是不同的。
這些特性有助于最優(yōu)化防御性編程的性能開銷。
asList方法
所有的immutable集合都以asList()的形式提供了ImmutableList視圖(view)。譬如,你把數(shù)據(jù)放在ImmutableSortedSet,你就可以調(diào)用sortedSet.asList().get(k)來取得前k個(gè)元素的集合。
返回的ImmutableList常常是個(gè)常數(shù)復(fù)雜度的視圖,而不是一個(gè)真的拷貝。也就是說,這個(gè)返回集合比一般的List更智能-譬如,它會(huì)更高效地實(shí)現(xiàn)contains這樣的方法。
實(shí)例:
@Test
public void testAsList(){
ImmutableList<String> imList=ImmutableList.of("peida","jerry","harry","lisa","jerry");
System.out.println("imList:"+imList);
ImmutableSortedSet<String> imSortList=ImmutableSortedSet.copyOf(imList);
System.out.println("imSortList:"+imSortList);
System.out.println("imSortList as list:"+imSortList.asList());
}
輸出:
imList:[peida, jerry, harry, lisa, jerry] imSortList:[harry, jerry, lisa, peida] imSortList as list:[harry, jerry, lisa, peida]
Guava集合和不可變對應(yīng)關(guān)系
| 可變集合類型 | 可變集合源:JDK or Guava? | Guava不可變集合 |
| Collection | JDK | ImmutableCollection |
| List | JDK | ImmutableList |
| Set | JDK | ImmutableSet |
| SortedSet/NavigableSet | JDK | ImmutableSortedSet |
| Map | JDK | ImmutableMap |
| SortedMap | JDK | ImmutableSortedMap |
| Multiset | Guava | ImmutableMultiset |
| SortedMultiset | Guava | ImmutableSortedMultiset |
| Multimap | Guava | ImmutableMultimap |
| ListMultimap | Guava | ImmutableListMultimap |
| SetMultimap | Guava | ImmutableSetMultimap |
| BiMap | Guava | ImmutableBiMap |
| ClassToInstanceMap | Guava | ImmutableClassToInstanceMap |
| Table | Guava | ImmutableTable |
總結(jié)
以上是生活随笔為你收集整理的Immutable(不可变)集合的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数学集合:N Z Q R C
- 下一篇: JDB调试