Guava集合--Immutable(不可變)集合

  所謂不可變集合,顧名思義就是定義了以後不可修改的集合。html

一.爲何要使用不可變集合

不可變對象有不少優勢,包括:java

  • 當對象被不可信的庫調用時,不可變形式是安全的;
  • 不可變對象被多個線程調用時,不存在競態條件問題
  • 不可變集合不須要考慮變化,所以能夠節省時間和空間。全部不可變的集合都比它們的可變形式有更好的內存利用率(分析和測試細節);
  • 不可變對象由於有固定不變,能夠做爲常量來安全使用。

建立對象的不可變拷貝是一項很好的防護性編程技巧。Guava爲全部JDK標準集合類型和Guava新集合類型都提供了簡單易用的不可變版本。
 JDK也提供了Collections.unmodifiableXXX方法把集合包裝爲不可變形式,但咱們認爲不夠好:git

  • 笨重並且累贅:不能溫馨地用在全部想作防護性拷貝的場景;
  • 不安全:要保證沒人經過原集合的引用進行修改,返回的集合纔是事實上不可變的;
  • 低效:包裝過的集合仍然保有可變集合的開銷,好比並發修改的檢查、散列表的額外空間,等等。

若是你沒有修改某個集合的需求,或者但願某個集合保持不變時,把它防護性地拷貝到不可變集合是個很好的實踐。編程

重要提示:全部Guava不可變集合的實現都不接受null值。咱們對Google內部的代碼庫作過詳細研究,發現只有5%的狀況須要在集合中容許null元素,剩下的95%場景都是遇到null值就快速失敗。若是你須要在不可變集合中使用null,請使用JDK中的Collections.unmodifiableXXX方法。更多細節建議請參考「使用和避免null」安全

 

二.JDK中實現immutable集合

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中的不可變集合

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

  運行結果:

 

 

比想象中更智能的copyOf

請注意,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)會試圖對以下狀況避免線性時間拷貝:

  • 在常量時間內使用底層數據結構是可能的——例如,ImmutableSet.copyOf(ImmutableList)就不能在常量時間內完成。
  • 不會形成內存泄露——例如,你有個很大的不可變集合ImmutableList<String>
    hugeList, ImmutableList.copyOf(hugeList.subList(0, 10))就會顯式地拷貝,以避免沒必要要地持有hugeList的引用。
  • 不改變語義——因此ImmutableSet.copyOf(myImmutableSortedSet)會顯式地拷貝,由於和基於比較器的ImmutableSortedSet相比,ImmutableSet對hashCode()和equals有不一樣語義。

在可能的狀況下避免線性拷貝,能夠最大限度地減小防護性編程風格所帶來的性能開銷。

asList視圖

全部不可變集合都有一個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));
    }
}

運行結果:

 

 

Guava集合和不可變對應關係

可變集合接口 屬於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
相關文章
相關標籤/搜索