能夠說 Java Collections Framework 知足了咱們大多數狀況下使用集合的要求,可是當遇到一些特殊的狀況咱們的代碼會比較冗長,比較容易出錯。Guava Collections 能夠幫助你的代碼更簡短精煉,更重要是它加強了代碼的可讀性。看看 Guava Collections 爲咱們作了哪些很酷的事情。java
固然,若是沒有 Guava Collections 你也能夠用 Java Collections Framework 完成上面的功能。可是 Guava Collections 提供的這些 API 通過精心設計,並且還有 25000 個單元測試來保障它的質量。因此咱們不必從新發明輪子。接下來咱們來詳細看看 Guava 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。工具
回頁首測試
你可能會說這和 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 接口的類有:
元素被排序存放於
TreeMap看到這裏你可能已經發現 Guava Collections 都是以 create 或是 of 這樣的靜態方法來構造對象。這是由於這些集合類大多有多個參數的私有構造方法,因爲參數數目不少,客戶代碼程序員使用起來就很不方便。並且以這種方式能夠返回原類型的子類型對象。另外,對於建立範型對象來說,這種方式更加簡潔。
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 接口的主要實現類有:
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 是用來構造 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); }});
要對集合排序或者求最大值最小值,首推 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。