所謂不可變集合,顧名思義就是定義了以後不可修改的集合。html
不可變對象有不少優勢,包括:java
建立對象的不可變拷貝是一項很好的防護性編程技巧。Guava爲全部JDK標準集合類型和Guava新集合類型都提供了簡單易用的不可變版本。
JDK也提供了Collections.unmodifiableXXX方法把集合包裝爲不可變形式,但咱們認爲不夠好:git
若是你沒有修改某個集合的需求,或者但願某個集合保持不變時,把它防護性地拷貝到不可變集合是個很好的實踐。編程
重要提示:全部Guava不可變集合的實現都不接受null值。咱們對Google內部的代碼庫作過詳細研究,發現只有5%的狀況須要在集合中容許null元素,剩下的95%場景都是遇到null值就快速失敗。若是你須要在不可變集合中使用null,請使用JDK中的Collections.unmodifiableXXX方法。更多細節建議請參考「使用和避免null」。安全
package collections; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; public class JDKImmutable { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("a"); list.add("b"); list.add("c"); System.out.println("list:" + list); List<String> unmodifiableList = Collections.unmodifiableList(list); System.out.println("unmodifiableList:" + unmodifiableList); list.add("d"); System.out.println("list add a item after list:"+list); System.out.println("list add a item after unmodifiableList:"+unmodifiableList); List<String> unmodifiableList1 = Collections.unmodifiableList(Arrays.asList("a", "b", "c")); System.out.println("unmodifiableList1:" + unmodifiableList1); unmodifiableList1.add("bb"); System.out.println("unmodifiableList add a item after list:"+unmodifiableList1); } }
運行結果:數據結構
說明:Collections.unmodifiableList實現的不是真正的不可變集合,當原始集合修改後,不可變集合也發生變化。不可變集合不能夠修改集合數據,當強制修改時會報錯,實例中的最後兩個add會直接拋出不可修改的錯誤。併發
總結一下JDK的Collections.unmodifiableXXX方法實現不可變集合的一些問題:性能
1.它用起來笨拙繁瑣你不得不在每一個防護性編程拷貝的地方用這個方法
2.它不安全:若是有對象reference原始的被封裝的集合類,這些方法返回的集合也就不是正真的不可改變。
3.效率低:由於它返回的數據結構本質仍舊是原來的集合類,因此它的操做開銷,包括併發下修改檢查,hash table裏的額外數據空間都和原來的集合是同樣的。測試
Guava提供了對JDK裏標準集合類裏的immutable版本的簡單方便的實現,以及Guava本身的一些專門集合類的immutable實現。當你不但願修改一個集合類,或者想作一個常量集合類的時候,使用immutable集合類就是一個最佳的編程實踐。ui
Immutable集合使用方法:
一個immutable集合能夠有如下幾種方式來建立:
1.用copyOf方法, 譬如, ImmutableSet.copyOf(set)
2.使用of方法,譬如,ImmutableSet.of("a", "b", "c")或者ImmutableMap.of("a", 1, "b", 2)
3.使用Builder類
例子:
package collections; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; import java.awt.*; import java.util.ArrayList; import java.util.List; public class GuavaImmutable { public static void main(String[] args) { 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); } }
運行結果:
請注意,ImmutableXXX.copyOf方法會嘗試在安全的時候避免作拷貝——實際的實現細節不詳,但一般來講是很智能的,好比:
package collections; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; import java.util.ArrayList; import java.util.List; public class GuavaImmutableCopyOf { public static void main(String[] args) { 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); } }
運行結果:
ImmutableXXX.copyOf(ImmutableCollection)會試圖對以下狀況避免線性時間拷貝:
在可能的狀況下避免線性拷貝,能夠最大限度地減小防護性編程風格所帶來的性能開銷。
全部不可變集合都有一個asList()方法提供ImmutableList視圖,來幫助你用列表形式方便地讀取集合元素。例如,你可使用sortedSet.asList().get(k)從ImmutableSortedSet中讀取第k個最小元素。
asList()返回的ImmutableList一般是——並不老是——開銷穩定的視圖實現,而不是簡單地把元素拷貝進List。也就是說,asList返回的列表視圖一般比通常的列表平均性能更好,好比,在底層集合支持的狀況下,它老是使用高效的contains方法。
package collections; import com.google.common.collect.ImmutableSortedSet; public class GuavaImmutableAsList { public static void main(String[] args) { ImmutableSortedSet<String> imSortSet=ImmutableSortedSet.of("aa","bb","cc","dd","ee"); System.out.println(imSortSet.asList().get(2)); } }
運行結果:
可變集合接口 | 屬於JDK仍是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 |