Guava Collections使用介紹[超級強大]

功能列舉

能夠說 Java Collections Framework 知足了咱們大多數狀況下使用集合的要求,可是當遇到一些特殊的狀況咱們的代碼會比較冗長,比較容易出錯。Guava Collections 能夠幫助你的代碼更簡短精煉,更重要是它加強了代碼的可讀性。看看 Guava Collections 爲咱們作了哪些很酷的事情。java

  • Immutable Collections: 還在使用 Collections.unmodifiableXXX() ? Immutable Collections 這纔是真正的不可修改的集合
  • Multiset: 看看如何把重複的元素放入一個集合
  • Multimaps: 須要在一個 key 對應多個 value 的時候 , 本身寫一個實現比較繁瑣 - 讓 Multimaps 來幫忙
  • BiMap: java.util.Map 只能保證 key 的不重複,BiMap 保證 value 也不重複
  • MapMaker: 超級強大的 Map 構造類
  • Ordering class: 你們知道用 Comparator 做爲比較器來對集合排序,可是對於多關鍵字排序 Ordering class 能夠簡化不少的代碼
  • 其餘特性

固然,若是沒有 Guava Collections 你也能夠用 Java Collections Framework 完成上面的功能。可是 Guava Collections 提供的這些 API 通過精心設計,並且還有 25000 個單元測試來保障它的質量。因此咱們不必從新發明輪子。接下來咱們來詳細看看 Guava Collections 的一些具體功能。程序員

回頁首緩存

Immutable Collections: 真正的不可修改的集合

你們都用過 Collections.unmodifiableXXX() 來作一個不可修改的集合。例如你要構造存儲常量的 Set,你能夠這樣來作 :安全

Set<String> set = new HashSet<String>(Arrays.asList(new String[]{"RED", "GREEN"})); 
 Set<String> unmodifiableSet = Collections.unmodifiableSet(set);

這看上去彷佛不錯,由於每次調 unmodifiableSet.add() 都會拋出一個 UnsupportedOperationException。感受安全了?慢!若是有人在原來的 set 上 add 或者 remove 元素會怎麼樣?結果 unmodifiableSet 也是被 add 或者 remove 元素了。並且構造這樣一個簡單的 set 寫了兩句長的代碼。下面看看 ImmutableSet 是怎麼來作地更安全和簡潔 :數據結構

ImmutableSet<String> immutableSet = <span style="color: rgb(255, 0, 0);">ImmutableSet.of</span>("RED", "GREEN");

就這樣一句就夠了,並且試圖調 add 方法的時候,它同樣會拋出 UnsupportedOperationException。重要的是代碼的可讀性加強了很多,很是直觀地展示了代碼的用意。若是像以前這個代碼保護一個 set 怎麼作呢?你能夠 :app

ImmutableSet<String> immutableSet = <span style="color: rgb(255, 0, 0);">ImmutableSet.copyOf</span>(set);

從構造的方式來講,ImmutableSet 集合還提供了 Builder 模式來構造一個集合 :less

Builder<String>  builder = <span style="color: rgb(255, 0, 0);">ImmutableSet.builder</span>(); 
 ImmutableSet<String> immutableSet = builder.add("RED").addAll(set).<span style="color: rgb(255, 0, 0);">build</span>();

在這個例子裏面 Builder 不但能加入單個元素還能加入既有的集合。ide

除此以外,Guava Collections 還提供了各類 Immutable 集合的實現:ImmutableList,ImmutableMap,ImmutableSortedSet,ImmutableSortedMap。工具

回頁首測試

Multiset: 把重複的元素放入集合

你可能會說這和 Set 接口的契約衝突,由於 Set 接口的 JavaDoc 裏面規定不能放入重複元素。事實上,Multiset 並無實現 java.util.Set 接口,它更像是一個 Bag。普通的 Set 就像這樣 :[car, ship, bike],而 Multiset 會是這樣 : [car x 2, ship x 6, bike x 3]。

譬如一個 List 裏面有各類字符串,而後你要統計每一個字符串在 List 裏面出現的次數 :

Map<String, Integer> map = new HashMap<String, Integer>(); 
 for(String word : wordList){ 
    Integer count = map.get(word); 
    map.put(word, (count == null) ? 1 : count + 1); 
 } 
 //count word 「the」
 Integer count = map.get(「the」);

若是用 Multiset 就能夠這樣 :

HashMultiset<String> multiSet = HashMultiset.create(); 
 multiSet.addAll(wordList); 
 //count word 「the」
 Integer count = multiSet.<span style="color: rgb(255, 0, 0);">count</span>(「the」);

這樣連循環都不用了,並且 Multiset 用的方法叫 count,顯然比在 Map 裏面調 get 有更好的可讀性。Multiset 還提供了 setCount 這樣設定元素重複次數的方法,雖然你能夠經過使用 Map 來實現相似的功能,可是程序的可讀性比 Multiset 差了不少。

經常使用實現 Multiset 接口的類有:

  • HashMultiset: 元素存放於 HashMap
  • LinkedHashMultiset: 元素存放於 LinkedHashMap,即元素的排列順序由第一次放入的順序決定
  • TreeMultiset:元素被排序存放於TreeMap
  • EnumMultiset: 元素必須是 enum 類型
  • ImmutableMultiset: 不可修改的 Mutiset

看到這裏你可能已經發現 Guava Collections 都是以 create 或是 of 這樣的靜態方法來構造對象。這是由於這些集合類大多有多個參數的私有構造方法,因爲參數數目不少,客戶代碼程序員使用起來就很不方便。並且以這種方式能夠返回原類型的子類型對象。另外,對於建立範型對象來說,這種方式更加簡潔。

回頁首

Multimap: 在 Map 的 value 裏面放多個元素

Muitimap 就是一個 key 對應多個 value 的數據結構。看上去它很像 java.util.Map 的結構,可是 Muitimap 不是 Map,沒有實現 Map 的接口。設想你對 Map 調了 2 次參數 key 同樣的 put 方法,結果就是第 2 次的 value 覆蓋了第 1 次的 value。可是對 Muitimap 來講這個 key 同時對應了 2 個 value。因此 Map 看上去是 : {k1=v1, k2=v2,...},而 Muitimap 是 :{k1=[v1, v2, v3], k2=[v7, v8],....}。

舉個記名投票的例子。全部選票都放在一個 List<Ticket> 裏面,List 的每一個元素包括投票人和選舉人的名字。咱們能夠這樣寫 :

//Key is candidate name, its value is his voters 
 HashMap<String, HashSet<String>> hMap = new HashMap<String, HashSet<String>>(); 
 for(Ticket ticket: tickets){ 
    HashSet<String> set = hMap.get(ticket.getCandidate()); 
    if(set == null){ 
        set = new HashSet<String>(); 
        hMap.put(ticket.getCandidate(), set); 
    } 
    set.add(ticket.getVoter()); 
 }

咱們再來看看 Muitimap 能作些什麼 :直接put,它會保存同key多值

HashMultimap<String, String> map = <span style="color: rgb(255, 0, 0);">HashMultimap.create</span>(); 
 for(Ticket ticket: tickets){ 
    map.put(ticket.getCandidate(), ticket.getVoter()); 
 }

就這麼簡單!

Muitimap 接口的主要實現類有:

  • HashMultimap: key 放在 HashMap,而 value 放在 HashSet,即一個 key 對應的 value 不可重複
  • ArrayListMultimap: key 放在 HashMap,而 value 放在 ArrayList,即一個 key 對應的 value 有順序可重複
  • LinkedHashMultimap: key 放在 LinkedHashMap,而 value 放在 LinkedHashSet,即一個 key 對應的 value 有順序不可重複
  • TreeMultimap: key 放在 TreeMap,而 value 放在 TreeSet,即一個 key 對應的 value 有排列順序
  • ImmutableMultimap: 不可修改的 Multimap

回頁首

BiMap: 雙向 Map

BiMap 實現了 java.util.Map 接口。它的特色是它的 value 和它 key 同樣也是不可重複的,換句話說它的 key 和 value 是等價的。若是你往 BiMap 的 value 裏面放了重複的元素,就會獲得 IllegalArgumentException。

舉個例子,你可能常常會碰到在 Map 裏面根據 value 值來反推它的 key 值的邏輯

for(Map.Entry<User, Address> entry : map.entreSet()){ 
    if(entry.getValue().equals(anAddess)){ 
        return entry.getKey(); 
    } 
 } 
 return null;

若是把 User 和 Address 都放在 BiMap,那麼一句代碼就獲得結果了:

return biMap.<span style="color: rgb(255, 0, 0);">inverse().get(anAddess)</span>;

這裏的 inverse 方法就是把 BiMap 的 key 集合 value 集合對調,所以 biMap == biMap.inverse().inverse()。

BiMap的經常使用實現有:

HashBiMap: key 集合與 value 集合都有 HashMap 實現

EnumBiMap: key 與 value 都必須是 enum 類型

ImmutableBiMap: 不可修改的 BiMap

回頁首

MapMaker: 超級強大的 Map 構造工具

MapMaker 是用來構造 ConcurrentMap 的工具類。爲何能夠把 MapMaker 叫作超級強大?看了下面的例子你就知道了。首先,它能夠用來構造 ConcurrentHashMap:獲得線程安全的hashMap

//ConcurrentHashMap with concurrency level 8 
 ConcurrentMap<String, Object> map1 = new MapMaker() 
    .<span style="color: rgb(255, 0, 0);">concurrencyLevel</span>(8) 
     .makeMap();

者構造用各類不一樣 reference 做爲 key 和 value 的 Map:

//ConcurrentMap with soft reference key and weak reference value 
 ConcurrentMap<String, Object> map2 = new MapMaker() 
    .softKeys() 
    .weakValues() 
    .makeMap();

或者構造有自動移除時間過時項的 Map:存放30秒後移除,能夠用於存放請求的場景

//Automatically removed entries from map after 30 seconds since they are created 
 ConcurrentMap<String, Object> map3 = new MapMaker() 
    <span style="color: rgb(255, 0, 0);">.expireAfterWrite(30, TimeUnit.SECONDS)</span> 
    .makeMap();

或者構造有最大限制數目的 Map:限制存放數據

//Map size grows close to the 100, the map will evict 
 //entries that are less likely to be used again 
 ConcurrentMap<String, Object> map4 = new MapMaker() 
    .<span style="color: rgb(255, 0, 0);">maximumSize(100) </span>
    .makeMap();

或者提供當 Map 裏面 get 不到數據,它可以自動加入到 Map 的功能。這個功能當 Map 做爲緩存的時候頗有用 :

//Create an Object to the map, when get() is missing in map 
 ConcurrentMap<String, Object> map5 = new MapMaker() 
    .<span style="color: rgb(255, 0, 0);">makeComputingMap</span>( 
      new Function<String, Object>() { 
        public Object apply(String key) { 
          return createObject(key); 
    }});

這些還不是最強大的特性,最厲害的是 MapMaker 能夠提供擁有以上全部特性的 Map:

//Put all features together! 
 ConcurrentMap<String, Object> mapAll = new MapMaker() 
    .<span style="color: rgb(255, 0, 0);">concurrencyLevel(8) 
    .softKeys() 
    .weakValues() 
    .expireAfterWrite(30, TimeUnit.SECONDS) 
    .maximumSize(100) 
    .makeComputingMap(</span> 
      new Function<String, Object>() { 
        public Object apply(String key) { 
          return createObject(key); 
     }});

回頁首

Ordering class: 靈活的多字段排序比較器

要對集合排序或者求最大值最小值,首推 java.util.Collections 類,但關鍵是要提供 Comparator 接口的實現。假設有個待排序的 List<Foo>,而 Foo 裏面有三個排序關鍵字 int a, int b 和 int c:

Collections.sort(list, new Comparator<Foo>(){    
    @Override    
    public int compare(Foo f1, Foo f2) {    
        int resultA = f1.a – f2.a; 
        int resultB = f1.b – f2.b; 
        return  resultA == 0 ? (resultB == 0 ? f1.c – f2.c : resultB) : resultA;

}});

這看上去有點眼暈,若是用一串 if-else 也好不到哪裏去。看看 ComparisonChain 能作到什麼 :

Collections.<span style="color: rgb(255, 0, 0);">sort</span>(list, new Comparator<Foo>(){    
    @Override 
    return <span style="color: rgb(255, 0, 0);">ComparisonChain.start()  
         .compare(f1.a, f2.a)  
         .compare(f1.b, f2.b) 
         .compare(f1.c, f2.c).result()</span>; 
         }});

若是排序關鍵字要用自定義比較器,compare 方法也有接受 Comparator 的重載版本。譬如 Foo 裏面每一個排序關鍵字都已經有了各自的 Comparator,那麼利用 ComparisonChain 能夠 :

Collections.sort(list, new Comparator<Foo>(){    
    @Override 
    return ComparisonChain.start()  
         .compare(f1.a, f2.a, comparatorA)  
         .compare(f1.b, f2.b, comparatorB) 
         .compare(f1.c, f2.c, comparatorC).result(); 
         }});

Ordring 類還提供了一個組合 Comparator 對象的方法。並且 Ordring 自己實現了 Comparator 接口因此它能直接做爲 Comparator 使用:

Ordering<Foo> ordering = Ordering.compound(\
     Arrays.asList(comparatorA, comparatorB, comparatorc)); 
 Collections.sort(list, ordering);

回頁首

其餘特性 :

過濾器:利用 Collections2.filter() 方法過濾集合中不符合條件的元素。譬如過濾一個 List<Integer> 裏面小於 10 的元素 :

Collection<Integer>  filterCollection = 
        Collections2.<span style="color: rgb(255, 0, 0);">filter</span>(list, new Predicate<Integer>(){ 
    @Override 
    public boolean <span style="color: rgb(255, 0, 0);">apply</span>(Integer input) { 
        return <span style="color: rgb(255, 0, 0);">input >= 10</span>; 
 }});

固然,你能夠本身寫一個循環來實現這個功能,可是這樣不能保證以後小於 10 的元素不被放入集合。filter 的強大之處在於返回的 filterCollection 仍然有排斥小於 10 的元素的特性,若是調 filterCollection.add(9) 就會獲得一個 IllegalArgumentException。

轉換器:利用 Collections2.transform() 方法來轉換集合中的元素。譬如把一個 Set<Integer> 裏面全部元素都轉換成帶格式的 String 來產生新的 Collection<String>:

Collection<String>  formatCollection = 
      <span style="color: rgb(255, 0, 0);">Collections2.transform(set, new Function<Integer, String>(){ 
    @Override 
    public String apply(Integer input) { 
        return new DecimalFormat("#,###").format(input)</span>; 
 }} );

回頁首

下載與使用

這個開源項目發佈的 jar 包能夠在它的官方網站內(http://code.google.com/p/guava-libraries/downloads/list)找到。其下載的 zip 包中含有 Guava Collections 的 jar 包 guava-r09.jar 及其依賴包 guava-r09-gwt.jar,javadoc,源代碼,readme 等文件。使用時只需將 guava-r09.jar 和依賴包 guava-r09-gwt.jar 放入 CLASSPATH 中便可。

若是您使用 Maven 做爲構建工具的話能夠在 pom.xml 內加入:

<dependency> 
    <groupId>com.google.guava</groupId> 
    <artifactId>guava</artifactId> 
    <version>r09</version> 
 </dependency>

須要注意的是本文介紹的 Guava r09 須要 1.5 或者更高版本的 JDK。

更多教程http://ifeve.com/google-guava/

相關文章
相關標籤/搜索