Google Guava Collections(如下都簡稱爲 Guava Collections)是 Java Collections Framework 的加強和擴展。每一個 Java 開發者都會在工做中使用各類數據結構,不少狀況下 Java Collections Framework 能夠幫助你完成這類工做。可是在有些場合你使用了 Java Collections Framework 的 API,但仍是須要寫不少代碼來實現一些複雜邏輯,這個時候就能夠嘗試使用 Guava Collections 來幫助你完成這些工做。這些高質量的 API 使你的代碼更短,更易於閱讀和修改,工做更加輕鬆。html
目標讀者java
對於理解 Java 開源工具來講,本文讀者至少應具有基礎的 Java 知識,特別是 JDK5 的特性。由於 Guava Collections 充分使用了範型,循環加強這樣的特性。做爲 Java Collections Framework 的加強,讀者必須對 Java Collections Framework 有清晰的理解,包括主要的接口約定和經常使用的實現類。而且 Guava Collections 很大程度上是幫助開發者完成比較複雜的數據結構的操做,所以基礎的數據結構和算法的知識也是清晰理解 Guava Collections 的必要條件。程序員
項目背景算法
Guava Collections 是 Google 的工程師 Kevin Bourrillion 和 Jared Levy 在著名"20%"時間寫的代碼。固然做爲開源項目還有其餘的開發者貢獻了代碼。在編寫的過程當中,Java Collections Framework 的做者 Joshua Bloch 也參與了代碼審覈和提出建議。目前它已經移到另一個叫 guava-libraries 的開源項目下面來維護。緩存
由於功能類似並且又同是開源項目,人們很很天然會把它和 Apache Commons Collections 來作比較。其區別歸結起來有如下幾點:安全
Guava Collections 充分利用了 JDK5 的範型和枚舉這樣的特性,而 Apache Commons Collections 則是基於 JDK1.2。其次 Guava Collections 更加嚴格遵照 Java Collections Framework 定義的接口契約,而在 Apache Commons Collections 你會發現很多違反這些 JDK 接口契約的地方。這些不遵照標準的地方就是出 bug 的風險很高。最後 Guava Collections 處於積極的維護狀態,本文介紹的特性都基於目前最新 2011 年 4 月的 Guava r09 版本,而 Apache Commons Collections 最新一次發佈也已是 2008 年了。數據結構
功能列舉app
能夠說 Java Collections Framework 知足了咱們大多數狀況下使用集合的要求,可是當遇到一些特殊的狀況咱們的代碼會比較冗長,比較容易出錯。Guava Collections 能夠幫助你的代碼更簡短精煉,更重要是它加強了代碼的可讀性。看看 Guava Collections 爲咱們作了哪些很酷的事情。less
固然,若是沒有 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 = ImmutableSet.of("RED", "GREEN"); |
就這樣一句就夠了,並且試圖調 add 方法的時候,它同樣會拋出 UnsupportedOperationException。重要的是代碼的可讀性加強了很多,很是直觀地展示了代碼的用意。若是像以前這個代碼保護一個 set 怎麼作呢?你能夠 :
ImmutableSet<String> immutableSet = ImmutableSet.copyOf(set); |
從構造的方式來講,ImmutableSet 集合還提供了 Builder 模式來構造一個集合 :
Builder<String> builder = ImmutableSet.builder(); ImmutableSet<String> immutableSet = builder.add("RED").addAll(set).build(); |
在這個例子裏面 Builder 不但能加入單個元素還能加入既有的集合。
除此以外,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.count(「the」); |
這樣連循環都不用了,並且 Multiset 用的方法叫 count,顯然比在 Map 裏面調 get 有更好的可讀性。Multiset 還提供了 setCount 這樣設定元素重複次數的方法,雖然你能夠經過使用 Map 來實現相似的功能,可是程序的可讀性比 Multiset 差了不少。
經常使用實現 Multiset 接口的類有:
元素被排序存放於
TreeMap看到這裏你可能已經發現 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 能作些什麼 :
HashMultimap<String, String> map = HashMultimap.create(); 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.inverse().get(anAddess); |
這裏的 inverse 方法就是把 BiMap 的 key 集合 value 集合對調,所以 biMap == biMap.inverse().inverse()。
BiMap的經常使用實現有:
HashBiMap: key 集合與 value 集合都有 HashMap 實現
EnumBiMap: key 與 value 都必須是 enum 類型
ImmutableBiMap: 不可修改的 BiMap
MapMaker 是用來構造 ConcurrentMap 的工具類。爲何能夠把 MapMaker 叫作超級強大?看了下面的例子你就知道了。首先,它能夠用來構造 ConcurrentHashMap:
//ConcurrentHashMap with concurrency level 8 ConcurrentMap<String, Object> map1 = new MapMaker() .concurrencyLevel(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:
//Automatically removed entries from map after 30 seconds since they are created ConcurrentMap<String, Object> map3 = new MapMaker() .expireAfterWrite(30, TimeUnit.SECONDS) .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() .maximumSize(100) .makeMap(); |
或者提供當 Map 裏面不包含所 get 的項,而須要自動加入到 Map 的功能。這個功能當 Map 做爲緩存的時候頗有用 :
//Create an Object to the map, when get() is missing in map ConcurrentMap<String, Object> map5 = new MapMaker() .makeComputingMap( new Function<String, Object>() { public Object apply(String key) { return createObject(key); }}); |
這些還不是最強大的特性,最厲害的是 MapMaker 能夠提供擁有以上全部特性的 Map:
//Put all features together! ConcurrentMap<String, Object> mapAll = new MapMaker() .concurrencyLevel(8) .softKeys() .weakValues() .expireAfterWrite(30, TimeUnit.SECONDS) .maximumSize(100) .makeComputingMap( 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.sort(list, new Comparator<Foo>(){ @Override return ComparisonChain.start() .compare(f1.a, f2.a) .compare(f1.b, f2.b) .compare(f1.c, f2.c).result(); }}); |
若是排序關鍵字要用自定義比較器,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.filter(list, new Predicate<Integer>(){ @Override public boolean apply(Integer input) { return input >= 10; }}); |
固然,你能夠本身寫一個循環來實現這個功能,可是這樣不能保證以後小於 10 的元素不被放入集合。filter 的強大之處在於返回的 filterCollection 仍然有排斥小於 10 的元素的特性,若是調 filterCollection.add(9) 就會獲得一個 IllegalArgumentException。
轉換器:利用 Collections2.transform() 方法來轉換集合中的元素。譬如把一個 Set<Integer> 裏面全部元素都轉換成帶格式的 String 來產生新的 Collection<String>:
Collection<String> formatCollection = Collections2.transform(set, new Function<Integer, String>(){ @Override public String apply(Integer input) { return new DecimalFormat("#,###").format(input); }} ); |
這個開源項目發佈的 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。