使用 Google Guava 美化你的 Java 代碼:1~4
1-使用 GOOGLE COLLECTIONS,GUAVA,STATIC IMPORTS 編寫漂亮代碼
寫在前面:
以前在一個項目中用到過guava,當時匆匆用,也沒細研究,今天偶然在occhina看到這個系列教程的翻譯,感覺不錯,介紹得還比較全面,就一口氣全看完了,但看到第四節,發現還沒翻譯,于是自己就硬著頭皮看了英文,發現也能看懂大概意思,就順手翻譯了一下,也算是為開源事業做點兒貢獻吧。把文章轉到自己博客記錄一下。
附:
Guava API
Guava 用法整理
這個夏天的早些時候,我已經極力向我的所有同事推薦了 Google Collections 。 Kevin Bourrillion說他的一個同事告訴他“沒有使用Google Collections前編程就像把一只手綁在背后”。
我灰常同意Kevin的這個同事!
可能文章的標題有點奇怪。我指的是“編寫漂亮代碼”。我猜我應該說“簡潔的”Java代碼,但是,它們意思可不一樣(譯者注:漂亮的代碼看著很爽,簡潔的不一定爽)。
在我準備開始賣力的吆喝這個我最最喜歡的Java類庫前,我有幾個額問題要問問你:
多少次你寫了像下面一樣的代碼:
Map<String, Map<Long, List<String>>> map = new HashMap<String, Map<Long,List<String>>>();
或者像這樣的不堪入目的代碼:
int a = 5; int b = 10; int compareTo = Integer.valueOf(a).compareTo(Integer.valueOf(b));
或者有很多的if和else :-(
又有多少次你寫了像下面這樣的代碼,只為了從一個文件中讀一點點東西?:
File file = new File(getClass().getResource("/test.txt").getFile()); BufferedReader reader; String text = ""; try { reader = new BufferedReader(new FileReader(file)); String line = null; while (true) { line = reader.readLine(); if (line == null) { break; } text += line.trim() + "\n"; } reader.close(); reader = null; } catch (FileNotFoundException e1) { e1.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }
好吧,我想說… 這都 TM 什么玩意?!
我們已經有Apache Commons Collections很多年了。那為什么我們還需要另外一個collections庫呢?我看過很多像這樣的評論:
“任何有一段時間開發經驗的Java程序員都會積累這些類型的實用的工具類”
好吧,是的,這對于大多數開發者來說可能是(應該是)對的。但是,有太多理由來擺脫垃圾代碼和重用漂亮的工具類!在這個博客里,我將要告訴你一些的確引起我求知欲-讓我沉溺于其中的事情,那就是Google Collections。
正如Jared Levy 曾說過的:
這個庫簡化了你的代碼,使它易寫、易讀、易于維護。它能提高你的工作效率,讓你從大量重復的底層代碼中脫身。
此外,我會給你們展示一下Guava里面包含的很多非常酷的功能,讓你們看看如何用它來寫出更漂亮的代碼,一旦看到這些,你們就會和以前那種僵化的令人討厭的寫代碼方式說拜拜了。
Google Guava 是 Google 為 Java 1.6 編寫的核心庫。它仍然不是一個很成熟的庫,在未來幾個月還將不斷的變化。Google Collections 將在 1.0 版本發布時將成為 Guava 的一部分。Guava (和 Google Collections) 已經率先被很多 Google 開發者使用。支持該項目的開發者有 Kevin Bourrillion, Jared Levy, Crazy Bob Lee, Josh Bloch(!) (Google 的 Java 首席架構師) 和 limpbizkit (我們找不到這家伙的真實姓名). Google Collections 在 2007 年就已經有了,但 Guava 是在 2009年9月推出的。
作為這個系列的博客,我將向你介紹 Google Collections 并告訴你使用 Guava 和 Google Collections 能為你帶來什么好處。包括代碼量的減少以及新的更快的數據結構。在第二部分我們將深入探討 Guava 和 Collections 的一些高級特性。
1、Google Collections一覽
顯然一篇博文不能深入地覆蓋Google Collections的方方面面,所以我決定把時間放在我日常編碼中使用到的基礎且不失強大的特性上,首先,不要這么做:
Map<String, Map<Long, List<String>>> map = new HashMap<String, Map<Long,List<String>>>();
要這么做:
Map<String, Map<Long, List<String>>> map = Maps.newHashMap();
或者更甚者直接使用靜態導入:
Map<String, Map<Long, List<String>>>map = newHashMap();
很棒,不是嗎?多虧有泛型和寫Collections的哥們提供給我們的這些方便工廠方法,我們不再需要寫一些Java本身應該自帶的東西。好吧,我知道這些會是JDK 7里的一部分,那當然很好,不過Google Collections現在就有這些功能。
類似于com.google.common.collect.Maps提供的這些靜態工具方法,Lists和Sets也有:
Lists.newArrayList(); Sets.newHashSet();
還有更多! 自己去看看吧![http://code.google.com/p/google-collections/]
2、操作lists和maps
當你在寫單元測試時,經常會構造一些測試數據,可能是list、map、set等,對于一些像我一樣草率的人來說,測試代碼中會經常看到類似下面的語句:
List<String> list = new ArrayList<String>(); list.add("a"); list.add("b"); list.add("c"); list.add("d");
其實我也知道,這幾行代碼看起來很爛,我只是想用一些測試數據構造一個不可變的list而已,我希望能像下面這樣寫一行代碼搞定這些。。如何辦到?好吧,這很簡單!
ImmutableList<String> of = ImmutableList.of("a", "b", "c", "d");
Map也一樣
ImmutableMap<String,String> map = ImmutableMap.of("key1", "value1", "key2", "value2");
我現在慢慢的越來越喜歡這種簡單而又有效的寫代碼的方式,我還想縮短一下上面的代碼,但是由于ImmutableList和ImmutableMap都有of方法,不能使用靜態導入。不過有一個變通的方法就是說在我們自己的集合工具中包裝這些創建工廠方法,那么對于不可變的list和map我只需要簡單的這么寫就好:
ImmutableMap<String,String> map2 = mapOf("key1", "value1", "key2", "value2")
或者
ImmutableList<String> list2 = listOf("a", "b", "c", "d");
而且如果我想構造填充一個ArrayList(或者一個HashMap),我可以這樣:
ArrayList<String> list3 = arrayListOf("a", "b", "c", "d");
兩種方式都可以,選擇權在你手上,很明顯,這種較之前面的方式更靈活優雅一些,你說呢? 除去可以使用方便干凈的方式來創建并填充集合類,我們也提供了很多額外的工具方法,比如過濾,對set取交集和并集,排序等等一些更優雅的方法,我將會在第二部分中講解它們。
3、靜態導入和Eclipse模板
在這之前,我想先告訴你我在寫代碼時,是如果借用Eclipse的Code模板功能更高效的使用集合類的。(我假設你了解在IDEA或者其他IDE中也存在類似的功能,你可以根據這部分敘述做出相應的擴展或變通)
作為一個Eclipse的用戶,我非常喜歡快捷鍵(參照這篇介紹Eclipse快捷鍵的博文:MouseFeed and how you can easily learn Eclipse shortcuts)
OK,Ctrl+space是你們的好朋友!他也是代碼自動補全的快捷鍵。(譯者注:當然。在中國,由于這個快捷鍵和切換輸入法沖突,一般都設置為了“Alt+/”作為代碼補全的快捷鍵)
在Eclipse下你可以創建一個模板來綁定到一個自動補全的快捷關鍵字上,這也是魔力所在!
相對于鍵入Maps.newHashMap()來創建一個HashMap,或者干脆使用靜態導入,就可以簡單的鍵入newHashMap()來創建一個HashMap,我只是簡單的敲入newH,按下ctrl+space,見證奇跡的時刻到了!
在Eclipse菜單欄選擇Window -> Preferences,進入Java -> Editor -> Templates,點擊“New”。Name處就是你想敲入的快捷關鍵字,我通常命名為我的方法名,比如在這里,就是“newHashMap”,加上你喜歡的描述,比如”Import static Maps.newHashMap“,并增加下面的內容:
${:importStatic(com.google.common.collect.Maps.newHashMap)}newHashMap();${cursor}
以上就是創建快捷補全的全部步驟了,現在去為你常用到的所有方法添加模板吧!
4、Guava走馬觀花
最后,但并非不重要,我將向你展示一下如果使用Guava來處理本文開頭留下來的兩個問題:
1.從文件中按行讀取內容:
File file = new File(getClass().getResource("/test.txt").getFile()); List<String> lines = null; try { lines = Files.readLines(file, Charsets.UTF_8); } catch (IOException e) { e.printStackTrace(); }
2.比較兩個基本類型:
int compare = Ints.compare(a, b);
3.把一個List轉換為int數組:
List<Integer> list = listOf(1, 2, 3, 4); int[] array2 = Ints.toArray(list);
Guava為我們提供了對Core Java類庫全面的擴展,我們可以使用com.google.common.primitices包下的Ints,Doubles,Floats,Shorts,Bytes以及Bools等工具類操作基本類型的數據;com.google.common.io包提供了操作streams,buffers以及files等等,而且并發包中提供了一些像Futures,Callables以及Executors等方便的工具類來減輕我們寫并發代碼的痛苦。除此之外,Guava提供了對Collections的增強,以及非常優雅的CharMatcher、Joiner以及Splitter類,這些類我將在下篇博文中提到。
可以從這里獲得源碼:
svn checkout http://guava-libraries.googlecode.com/svn/trunk/guava-libraries-read-only
下次我們會深入Guava 的高級功能,探索一下集合的用法,看看怎樣通過Multimap使java的功能更加強大,如何用mapping功能來轉換集合。請拭目以待。如果你們也用過Guava或者Google Collections請分享一下你們的心得。
2-深入探索 GOOGLE GUAVA 庫
在這個系列的第一部分里,我簡單的介紹了非常優秀的Google collections和Guava類庫,并簡要的解釋了作為Java程序員,如果使用Guava庫來減少項目中大量的樣板代碼。在這篇博文中我們將深入挖掘Guava提供的更高級的特性。 我們將深入挖掘Guava庫,并了解一下優雅的CharMatcher類、Joiner以及Splitter類,以及在處理Java基本類型時Guava給我們帶來的別的工具類。
1、The Guava CharMatcher
CharMatcher 可以非常方便地添加到你的java工具箱中。有些人形容它:“像打了興奮劑的StringUtils”:p 你可以使用預先設定好的常量,比如CharMatcher.WHITESPACE, CharMatcher.JAVA_DIGIT 或者CharMatcher.ASCII,此外你還有很多簡便的工廠方法如CharMatcher.is(‘aaa’), CharMatcher.isNot(‘bbb’), CharMatcher.oneOf(‘abcd’).negate(),甚至更復雜一些比如:
CharMatcher.inRange('a', 'z').or(inRange('A', 'Z'));
當然你可以繼承它然后實現方法 matches(char c)。你可以把 Google Collection中都創造實現一遍(當然下次我們會覆蓋到)!
如果你想從字符串中得到所有的數字,那么你可以這樣:
String string = CharMatcher.DIGIT.retainFrom("some text 89983 and more");
如果你想把字符串中的數據都去掉,可以如下:
String string = CharMatcher.DIGIT.removeFrom("some text 89983 and more");
還有好多匹配的方法:
matchesAllOf(CharSequence)
atchesAnyOf(CharSequence)
matchesNoneOf(CharSequence)
除了indexIn, lastIndexIn and countIn這些方法,還有很多trimming, replacing and collapsing相關的方法。 更多信息查看Java doc.
2、Joiner and Splitter
目前Joiner還是Collections 的一部分,Splitter 已經加入了Guava (不過一旦Collections 發布1.0版本,那么它們會一起加入到Guava)。
可以這么使用Joiner:
String[] subdirs = { "usr", "local", "lib" }; String directory = Joiner.on("/").join(subdirs);
或者這樣:
int[] numbers = { 1, 2, 3, 4, 5 }; String numbersAsString = Joiner.on(";").join(Ints.asList(numbers));
得益于Guava對基本型的支持,可以很方便這么處理:
String numbersAsStringDirectly = Ints.join(";", numbers);
對于字符串,我們可以直接進行分割,但是這樣做多少有些奇怪, Splitter 提供了更多的操作,而且更加健壯。字符創分割通常返回的是一個數組而 Splitter 返回的是一個迭代 Iterable。
Iterable split = Splitter.on(",").split(numbsAsString);
你可以簡單地分割字符串:
String[] splitRegular = testString.split(",");
但是當需要處理下面這樣的字符串時:
String testString = "foo , what,,,more,";
輸出結果是:
‘foo ‘
‘ what’
”
”
‘more
這樣的結果也許還可以,但是Splitter允許我們對分割結果做更多的控制:
Iterable<String> split = Splitter.on(",").omitEmptyStrings().trimResults().split(testString);
得到的結果是 foo’,'what’,'more’
Joiner和Splitter都是可配置的,甚至你可以把Joiner使用在map中。
最后再看個幾個例子加深印象吧:
String str = "test1, , test2, test3"; Iterable<String> strArr = Splitter.on(',') .trimResults() .omitEmptyStrings() .split(str); Output:-> ["test1", "test2", "test3"]
String str = "key1: 1; key2: 2 ; key3: 3"; Map<String, String> m = Splitter.on(';') .trimResults() .withKeyValueSeparator(":") .split(str); Output:-> {key1= 1, key2= 2, key3= 3} private static final Splitter COMMA_SPLITTER = Splitter.on(',') .trimResults() .omitEmptyStrings(); COMMA_SPLITTER.split("foo, ,bar, quux,"); Output:-> ["foo", "bar", "quux"]
ArrayList<String> strArr1 = Lists.newArrayList( "test1","test2","test3",null,"test4",null,null); Joiner.on(';') .skipNulls() .join(strArr1); Output:-> "test1;test2;test3;test4" Joiner.on(';') .useForNull("_") .join(strArr1); Output:-> "test1;test2;test3;_;test4;_;_"
Map<String, String> map = Maps.newHashMap(); map.put("key1", "value1"); map.put("key2", "value2"); map.put("key3", null); map.put("key4", "value3"); Joiner.on(';') .useForNull("NULL") .withKeyValueSeparator("=") .join(map); Output:-> "key4=value3;key3=NULL;key2=value2;key1=value1"
ArrayList<String> strArr = Lists.newArrayList( " test1","test2 "," test3 ",null,"test4",null,null,"", " "); Predicate<String> EMPTY_OR_NULL_FILTER = new Predicate<String>() { @Override public boolean apply(String str){ str = Strings.nullToEmpty(str).trim(); return !Strings.isNullOrEmpty(str); } }; Function<String, String> TRIM_RESULT = new Function<String, String>(){ @Override public String apply(String str){ return Strings.nullToEmpty(str).trim(); } }; String joinStr = Joiner.on(';') .skipNulls() .join(Collections2.transform(Collections2.filter(strArr, EMPTY_OR_NULL_FILTER), TRIM_RESULT)); Output:-> "test1;test2;test3;test4"
public static void main(String[] args) { String tmpValue = "a_b_c_1_2_3"; String[] valArr = tmpValue.split("_"); // 求字符串數組的子串,并最后拼接起來 String tmpVal = ""; for (int i = 1; i < valArr.length; i++) { tmpVal = tmpVal.equalsIgnoreCase("") ? valArr[i] : tmpVal + "_" + valArr[i]; } System.out.println(tmpVal); System.out.println("———————"); // 上面這么一段與下面這句等價 System.out.println(Joiner.on("_").join(Lists.newArrayList(valArr).subList(1, valArr.length))); } // 結果: b_c_1_2_3 ——————— b_c_1_2_3
3、Working with primitives cont’d
在博客的第一部分,我簡單提到了基本型的用法。Guava已經提供了諸如Ints.compare(a, b)和Ints.toArray(list)。
讓我介紹Guava 關于基本型提供的更多的一些用法吧。
假如我有一個整型數字數組,我們想知道數組中是否有特定的整型數字。傳統的寫法如下:
int[] array = { 1, 2, 3, 4, 5 }; int a = 4; boolean hasA = false; for (int i : array) { if (i == a) { hasA = true; } }
使用Guava,我們可以如下:
boolean contains = Ints.contains(array, a);
同樣,其他類型的基本型數組也可以這么來做。我們甚至可以直接對數組做如下的事:
int indexOf = Ints.indexOf(array, a); int max = Ints.max(array); int min = Ints.min(array); int[] concat = Ints.concat(array, array2);
在這個系列的下一章我們將關注下 Google Collections library包的更高級特性如:Functions, Filtering and Ordering!歡迎繼續收看,請與我們分享你的看法。
3-JAVA 的函數式編程,通過 GOOGLE COLLECTIONS 過濾和調用
在本系列博客的第一、二部分,我介紹了非常優秀的Google Collections和Guava包。本篇博客中我們來看看如何使用Google Collections來做到過濾和排序功能。此外,我會帶你看看Google Collections是如何使Java有一點點“functional(方法化)”的進步了。
1、Functions, Functions, Functions!!
Google Collections給我們帶來了一對非常優雅的東東,叫做:Functions and Predicates! 和你使用的scala一樣有神奇的地方,現在你可以使用在no-functional 的java身上了。你可以在com.google.common.base包里找到這些(更多)。
我們將在下一部分過濾集合的時候談到Predicates類,首先我們先看一下Function的用法!
Google collections提供了Function接口,實際上,一個function就是從一個對象到另外一個對象的轉換變形。
像Lists和Maps這類的Collection工具類給我們提供了轉換的方法:
topMap = Maps.transformValues(fromMap, function); toList = Lists.transform(fromList, function);
舉個例子來說,假設你有一個Map,key是物品,value是對應的價格,單位是歐元。那么,你有個需求是將里面的價格都轉換為美元,傳統的做法是遍歷整個Map,然后更新每個value值,將價格轉換為美元價格,好麻煩...
有了Functions,世界一下子變清凈了...
Map usdPriceMap = Maps.transformValues(eurPriceMap, new Function() { double eurToUsd = 1.4888; public Double apply(final Double from) { return from * eurToUsd; } });
好吧,你可能說匿名內部類搞的有點糟,因為 你可能想重用這個function---這里只是演示函數式的一些特點而已。
和這個類似的,我們也可以使用Functions來把一個對象轉換成一個完全不同的對象,比如將一個整形轉換為字符串。
我們稍后再深入Functions類,首先我們瀏覽一下Multimap集合以及我們如果使用一點function來轉換一個集合。
2、使用條件過濾集合
我在Integrasco做數據工作時遇到的最常見的任務是過濾數據和對大集合進行排序。 簡單起見,我們假設你有一個姓名列表想要過濾(看起來有點幼稚):
List<String> names = listOf("Aleksander", "Jaran", "Integrasco", "Guava", "Java");
我們可以使用com.google.common.collect.Iterables和com.google.common.base.Predicates類來過濾例子中的列表,使過濾后的列表中只包含Aleksander或者Jaran:
現在我知道這聽起來有點傻帽,但是你仍然可以實現自己的Predicates條件類,比如返回名字長度小于5的列表(我從codemonkeyism.com偷到了下面這個例子):
Iterable<String> filtered = filter(names, or(or(equalTo("Aleksander"),equalTo("Jaran")), lengthLessThan(5)));
這個例子返回的是Aleksander,Jaran以及Java(因為Java的長度小于5)。or條件的部分讀起來有點繞,但我還可以忍受。equalTo以及or條件都是Predicates自帶的方法,用起來很方便。
現在我們來實現一個lengthLessThan的條件,我們只需要這么做:
private static class LengthLessThanPredicate implements Predicate<String> { private final int length; private LengthLessThanPredicate(final int length) { this.length = length; } public boolean apply(final String s) { return s.length() < length; } }
把這個實現在你的工具類里包裝一下就像這樣:
public static Predicate<String> lengthLessThan(final int length) { return new LengthLessThanPredicate(length); }
關注一下Stephan的博文fluent interfaces for Google Collections --寫的相當優雅~!
3、對集合排序
多虧有了java Collections類,我們可以這么排序:
Collections.sort(List<T>, Comparator<? super T>)
但有時候我們想做更復雜一些的事情,比如合并多個Comparator或者我們可能只是想要排序過的集合的一個視圖,而不改變原來集合的順序。
Google Collections給我們提供了Ordering,讓我們更好地掌控排序。假如我們有兩個對Person類排序的comparator,一個是根據lastName排序,一個是根據firstName排序:
Comparator<Person> byLastName = new Comparator<Person>() { public int compare(final Person p1, final Person p2) { return p1.getLastName().compareTo(p2.getLastName()); } } Comparator<Person> byFirstName = new Comparator<Person>() { public int compare(final Person p1, final Person p2) { return p1.getFirstName().compareTo(p2.getFirstName()); } };
那么,假如我們現在想現根據last name排序,再根據first name排序,然后對排序結果反序,那我們我們需要做的是:
List<Person> sortedCopy = Ordering.from(byLastName).compound(byFirstName).reverse().sortedCopy(persons);
而且,你甚至無需創建Comparator比較器,你可以直接擴展Ordering類!
List<Person> sortedCopy = orderByLastName.compound(orderByFirstName).reverse().sortedCopy(persons);
4、繼續過濾和排序
在這個系列的第一部分,Steve提到了在Scala中,你可以這么做:
people.filter(_.firstName == "Steve").sort(_.lastName < _.lastName)
功能是說從people這個集合中篩選出firstName為“Steve”的,并且按他們的lastName屬性排序。
從語法上來看,這行代碼非常優雅!那么我們也來看一下使用Google Collections時應該怎么做。Google Collections給我們提供了前面提到的Iterables類,我們可以使用Iterables類來實現過濾和轉換(你可以使用Google Collections里的Collections2來實現同樣的功能)。
那現在我們就來看一下如何實現和Steve的那行Scala代碼一樣的功能,雖然看起來沒有Scala實現的那么優雅,但卻是使用Predicates和Ordering類來實現上面功能的一種方式。
Ordering.from(byLastName).sortedCopy(filter(persons, withFirstName("Steve")));
雖然跟Scala提供的語法糖有點差距(很明顯我們還是需要我們的“withFirstName”條件謂詞以及“byLastName”比較器),但至少這比我們不使用Google Collections接近多了!(如果采用Stephen的流式接口的話,代碼會更易讀)。
Kevin Bourrillion在另一篇關于使用Google Collections編寫函數式的Java的文章中提到:
語法很爛。而且同時這在語言本身改變之前只是權宜之計,到那時我們才真正的選擇最佳的語法并開始真正的函數式編程。所以我還沒決定我會投入多大的努力到Function/Predicate中去(這段真的很難翻譯..我去..)
在下篇也是最后一篇關于Google Collections和Guava的博文中,我們將看到如果使用Google Collections操作Set和Map,以及使用Preconditiones來做驗證,而且我們帶你體驗一下奇妙的Multimap集合類以及如何進行切分!拭目以待吧!
4-條件,多重映射和分片
在本系列博客的前三章,我們大概介紹了Google的Guava類庫和Collections類庫,作為一名Java開發人員,相信你會從使用這些類庫,進而來減少在你項目中使用樣板文件的數量而獲益。在本系列博客的最后一篇中,我將帶大家來了解一個會讓你完全愛上并沉浸于其中的的集合工具類-Multimap。我們將帶大家了解下如何使用Google Collections的Preconditions來做校驗,但是在此之前,先讓我們來了解下如何對Set和Map進行交集、并集和差集的運算。
1、Set的交集,并集和map的相關操作
有時,當我們需要去對兩個Set的取交集、并集和差集的時候,那是一件相關麻煩的事情,而且代碼看起來很凌亂。經常情況,你會以一遍又一遍的循環來結束這種做法。但是如果我們使用Google Collections提供的Sets類就可以輕而易舉的完成這些操作,并且是完全免費的!
HashSet setA = newHashSet(1, 2, 3, 4, 5); HashSet setB = newHashSet(4, 5, 6, 7, 8); SetView union = Sets.union(setA, setB); System.out.println("union:"); for (Integer integer : union) System.out.println(integer); SetView difference = Sets.difference(setA, setB); System.out.println("difference:"); for (Integer integer : difference) System.out.println(integer); SetView intersection = Sets.intersection(setA, setB); System.out.println("intersection:"); for (Integer integer : intersection) System.out.println(integer);
From the Public Object blog:
“不同于慣例,這些方法沒有做任何的拷貝。相反,他們返回了代表了這兩個集合的視圖。 但是在有些情況下,這些拷貝又很有用,我們可以用immutableCopy類中提供的一個便利方法來實現拷貝”
同樣地,對于Map而言,我們可以像下面這樣處理:
MapDifference differenceMap = Maps.difference(mapA, mapB);
如果用MapDifference類,我們還可以這樣:
differenceMap.areEqual(); Map entriesDiffering = differenceMap.entriesDiffering(); Map entriesOnlyOnLeft = differenceMap.entriesOnlyOnLeft(); Map entriesOnlyOnRight = differenceMap.entriesOnlyOnRight(); Map entriesInCommon = differenceMap.entriesInCommon();
Thank you, Google Collections!
2、用Preconditions進行校驗
早在初夏的時候,一個叫剛剛加入我們的同事Bent André向我介紹了用Preconditions進行校驗的想法,但是,直到最近我才發現Google Collections實際上已經包含了相應的實現(我們自己也已經有了相似的實現)。
那么這個實現具體是什么樣子的呢?校驗就是要求我們準確無誤的做一些事情,通常情況下,它看起來就是類似下面這樣的代碼:
if (count <= 0) { throw new IllegalArgumentException("must be positive: " + count); }
我們想校驗調用我們的方法的方法傳參是否正確,如果錯了,應該發出一個“他出錯了”的警告信息。 JavaDoc中是這樣解釋的:
“Precondition異常通常表示被調用方法發生了一個錯誤。它告訴調用者不能用這種方式、這種參數或者其它來調用這個方法。Postcondition或其它運行失敗是不會拋出這種類型的異常的。”
因此用Preconditions類或者靜態導入,我們就可以用下面這行簡單的代碼來替代上面那些的冗長的代碼了:
checkArgument(count > 0, "must be positive: %s", count);
同樣,該類里還有其它類似的方法來檢驗狀態和空值。
checkNotNull(entry);
相當簡潔,不是么?
“請注意這里是相反的表達(譯者注:應該指的是checkNotNull中有not的意思,所以是相反的); 你用Preconditions聲明什么是你期望返回true的,就好像你在Java中運用斷言,或者在JUnit中調用assertTrue方法一樣。”
From the excellent blog series on Google Collections over at Public Object:
“Preconditions提供對方法狀態的校驗。它能使你輸入的參數如你所期望的的那樣足夠簡潔,與Java內建的斷言不同的是Preconditions是一直開啟的。”
下面是我在Integrasco上翻遍我所有的代碼中,找到的一個比較好的例子:
public PostExample(final String title, final Date date, final String author) { //some code to validate that title is not null //some code to validate that date is not null //some code to validate that author is not null this.title = title; this.date = date; this.author = author; }
用了Preconditions之后我們再來看看代碼變得有多工整:
public PostExample(final String title, final Date date, final String author) { this.title = checkNotNull(title); this.date = checkNotNull(date); this.author = checkNotNull(author); }
很簡潔,不是么?
稍后我們會發表一篇關于異常和校驗的更詳細的文章,敬請關注!
3、一個集合統治一切 – Multimap
我已經數不清我有多少次需要在一個Map中實現一個key對應多個value的需求,最終不得不以Map<K, List<V>>這種結構來實現的經歷了。 并且用這種方式實現一鍵多值的需求還存在很大的爭議,如下所示:
Map<Person, List<BlogPost>> map = new HashMap<Person, List<BlogPost>>(); public void addBlogPost(final Person author, final BlogPost blogPost) { List<BlogPost> blogPosts = map.get(author); if (blogPosts == null) { blogPosts = new ArrayList<BlogPost>(); map.put(author, blogPosts); } blogPosts.add(blogPost); }
別告訴你你從來沒這樣做過?利用Google Collections中的Multimap類我們可以輕松實現上述需求,而且感覺很時髦(我很開心我可以這樣表達):
Multimap<Person, BlogPost> multimap = ArrayListMultimap.create(); public void addBlogPost(final Person author, final BlogPost blogPost) { multimap.put(author, blogPost) }
Whoop, whoop!
Google Collections提供了多種Multimaps的實現,如果你想防止出現鍵值對,可以用HashMultimap;如果你需要鍵值對按照自然順序排列,你可以使用TreeMultimap;甚至你想按插入順序來遍歷集合,LinkedHashMultimap可以滿足你的需求。
Multimaps同樣提供了很多有用的方法,詳細內容可以參考Multimap 和 Multimaps的API文檔!很激動是么?那就繼續往下看吧...
4、分片集合
我管這節叫分片,相信很多人會覺得叫我們在之前文章叫提到的“過濾集合”更準確。但是當我們把把Multimap的概念和功能相結合的時候,你就會意識到他到底有多強大了,而不僅僅是過濾那么簡單!
我們假設我們已經擁有了包含了一組map的list。list里的每一個Map代表擁有指定屬性的一個文檔。這個Map看起來可能會是下面這個樣子:
mapOf("type", "blog", "id", "292", "author", "john");
即每個Map中我們擁有3個屬性,他們分別是“type”、 “id”和“author”。
如上所示,所有我們的List看起來應該是下面這個樣子:
List<Map<String, String>> listOfMaps
現在,我們想把這個list根據所裝載對象的類型不同分成多個list,比如一個叫“blog”,一個叫“news”等等...
如果沒有Google Collections這將是一場惡夢!我們必須得先循環這個list,然后再分別檢查每一個map中的key,然后再把根據類型的不同放入不同的list中。但如果我們不知道map里都有哪些類型,這個過程會更痛苦!
想不想和我一起來看看有沒有輕松的辦法解決?
用一點點Function的魔法加上Multimaps,我樣可以以一種上相當優雅的方式來解決這個問題:
Multimap<String, Map<String, String>> partitionedMap = Multimaps.index(listOfMaps, new Function<Map<String, String>, String>() { public String apply(final Map<String, String> from) { return from.get("type"); } });
現在我們擁有了每一個key代表不同類型的Multimaps了!
如果現在我們想要指定類型的所有map,唯一需要做的就是找Multimaps要!
好,就到這里為止了!希望你喜歡這段旅程,同時也希望你能從Google這些真正偉大的項目中受到啟發!
歡迎反饋!
Refer:
https://github.com/ecchanger/myblog/tree/master/posts
http://macrochen.iteye.com/blog/737058
http://www.oschina.net/search?scope=translate&q=Google+Collections
http://www.ibm.com/developerworks/cn/java/j-lo-googlecollection/
http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/index.html
http://techneerajnandwana.blogspot.com/2011/11/guava-string-manipulation-with.html
Apache Commons:
http://commons.apache.org/proper/commons-lang/javadocs/api-release/index.html
簡介:該工具類集合是由 apache 負責維護的,里面對多種常見的 java 操作做了封裝,如 讀寫文件、發 email、jdbc 訪問數據庫、文件上傳、圖像處理、字符串/集合/xml 的處理等,讓你告別繁瑣的 java 操作~