標籤:
轉自:http://www.cnblogs.com/renchunxiao/p/3661918.html?utm_source=tuicool
使用Joiner類將任一字元串通過分隔字元進行串連到一起是大多程式員經常做的事情。他們經常使用array,list,iterable並且迴圈變數將每一個臨時變數添加到StringBuilder當中去,並且中間添加分隔字元。這些笨重的處理方式如下:
public String buildString(List<String> stringList, String delimiter){ StringBuilder builder = new StringBuilder(); for (String s : stringList) { if(s !=null){ builder.append(s).append(delimiter); } } builder.setLength(builder.length() – delimiter.length()); return builder.toString();}
注意要刪除在最後面的分隔字元。不是很難懂,但是使用Joiner類可以得到簡單的代碼模板。同樣的例子使用Joiner類代碼如下:
public String buildString(List<String> stringList, String delimiter){ return Joiner.on(delimiter).skipNulls().join(stringList);}
這樣更加簡明並且不會出錯。如果你想將null值替換掉,可以使用如下方法:
Joiner.on("|").useForNull("no value").join(stringList);
使用Joiner類有幾點需要注意。Joiner類不僅僅可以處理字串的array、list、iterable,他還可以處理任何對象的array、list、iterable。結果就是調用每一個元素的toString()方法。因此,如果沒有使用skipNulls或者useForNull,就會拋出null 指標異常。Joiner對象一旦被建立就是不可變的,所以他們是安全執行緒的,可以被當作常亮來看待。然後看看下面的程式碼片段:
Joiner stringJoiner = Joiner.on("|").skipNulls(); //使用useForNull方法將會返回一個新的Joiner執行個體 stringJoiner.useForNull("missing"); stringJoiner.join("foo","bar",null);
在上面的代碼執行個體當中,useForNull方法並沒有起作用,null值仍然報NPE了。
Joiner不僅僅能返回字串,還可以與StringBuilder一起使用:
StringBuilder stringBuilder = new StringBuilder();Joiner joiner = Joiner.on("|").skipNulls();//返回的StringBuilder執行個體當中包含串連完成的字串joiner.appendTo(stringBuilder,"foo","bar","baz");
上面的例子,我們傳入一個StringBuilder的參數並且返回一個StringBuilder執行個體。
Joiner類也可以使用實現了Appendable介面的類。
FileWriter fileWriter = new FileWriter(new File("path")):List<Date> dateList = getDates();Joiner joiner = Joiner.on("#").useForNulls(" ");// 返回由字串拼接後的FileWriter執行個體 joiner.appendTo(fileWriter,dateList);
這是一個與上一個相似的例子。我們傳入一個FileWriter執行個體和一組資料,就會將這組資料拼接後附加到FileWriter當中並且返回。
我們可以看到,Joiner使一些公用的操作變的非常簡單。有一個特殊的方法會實現MapJoiner方法,MapJoiner方法像Joiner一樣使用分割符將每組key與value分開,同時key與value之間也有個分隔字元。MapJoiner方法的建立如下:
MapJoiner mapJoiner = Joiner.on("#").withKeyValueSeparator("=");
快速回顧一下上面內容:
- Joiner.on("#")方法會建立一個Joiner的執行個體。
- 使用返回的Joiner執行個體調用withKeyValueSeparator方法將會返回MapJoiner對象。
下面是對MapJoiner方法的單元測試代碼:
@Testpublic void testMapJoiner() { String expectedString = "Washington D.C=Redskins#New York City=Giants#Philadelphia=Eagles#Dallas=Cowboys"; Map<String,String> testMap = Maps.newLinkedHashMap(); testMap.put("Washington D.C","Redskins"); testMap.put("New York City","Giants"); testMap.put("Philadelphia","Eagles"); testMap.put("Dallas","Cowboys"); String returnedString = Joiner.on("#"). withKeyValueSeparator("=").join(testMap); assertThat(returnedString,is(expectedString));}
回顧時間
上面的單元測試開始時建立一個key和value都是字串的LinkedHashMap執行個體,值得注意的是我們使用靜態Factory 方法newLinkedHashMap()來建立,Maps類是在com.google.common. collect包當中。然後使用Joiner類建立一個使用key與value拼接的字串。最後我們斷言他是否與我們期望返回的字串相同。
使用Splitter類程式員另一個經常處理的問題是對字串以特定分隔字元進行分割並且擷取一個字串數組。如果你需要讀取文字檔,你總是會做這樣的事情。但是String.split方法不夠完美,看下面的例子:
String testString = "Monday,Tuesday,,Thursday,Friday,,";//parts is [Monday, Tuesday, , Thursday,Friday]String[] parts = testString.split(",");
可以看到,String.split方法省略了最後的2個空串。在有些時候,這個做法是你需要的,但是這些事情是應該由程式員來決定是否省略。Splitter類可以協助我們實現與Joiner類相反的功能。Splitter可以使用單個字元、固定字串、Regex串、Regex對象或者CharMatcher對象(另一個Guava的類,本章會講到)來分割字串。可以給定具體分割符來建立Splitter對象然後使用。一旦擁有了Splitter執行個體後就可以調用split方法,並且會返回包含分割後字串的迭代器對象。
Splitter.on(‘|‘).split("foo|bar|baz");Splitter splitter = Splitter.on("\\d+");
在上面的例子當中,我們看到一個Splitter 執行個體使用了‘|‘字元分割,另外一個執行個體使用了Regex進行分割。
Splitter有一個可以處理前面空格和後面空格的方法是trimResults。
//Splits on ‘|‘ and removes any leading or trailing whitespaceSplitter splitter = Splitter.on(‘|‘).trimResults();
與Joiner類一樣Splitter類同樣是一個不可變的類,所以在使用的時候應該使用調用trimResults方法後返回的Splitter執行個體。
Splitter splitter = Splitter.on(‘|‘);//Next call returns a new instance, does notmodify the original!splitter.trimResults();//Result would still contain empty elementsIterable<String> parts = splitter.split("1|2|3|||");
Splitter 類,像Joiner與MapJoiner一樣也有MapSplitter類。MapSplitter類可以將字串轉換成Map執行個體返回,並且元素的順序與字串給定的順序相同。使用下面方法構造一個MapSplitter執行個體:
//MapSplitter is defined as an inner class of SplitterSplitter.MapSplitter mapSplitter = Splitter.on("#"). withKeyValueSeparator("=");可以看到MapSplitter的建立方式與MapJoiner一樣。首先給Splitter指定一個分隔字元,然後指定MapSplitter對象key與value的分隔字元。下面是一個關於MapSplitter的例子,實現的是與MapJoiner相反地功能。
@Testpublic void testSplitter() { String startString = "Washington D.C=Redskins#New York City=Giants#Philadelphia=Eagles#Dallas=Cowboys"; Map<String,String> testMap = Maps.newLinkedHashMap(); testMap.put("Washington D.C","Redskins"); testMap.put("New York City","Giants"); testMap.put("Philadelphia","Eagles"); testMap.put("Dallas","Cowboys"); Splitter.MapSplitter mapSplitter = Splitter.on("#").withKeyValueSeparator("="); Map<String,String> splitMap = mapSplitter.split(startSring); assertThat(testMap,is(splitMap));}
回顧時間上面的單元測試是將一個字串用MapSplitter分割成一個LinkedHashMap的執行個體。然後斷言這個返回的LinkedHashMap執行個體是否與我們期望是LinkedHashMap執行個體相同。Joiners 和Splitters 這兩個類應該是每個java開發人員的工具。
使用Guava操作字串無論你喜歡的哪種語言,所有跟字串相關的事情都是無聊乏味的並且容易出錯。有時候,我們需要從檔案或者資料當中讀取資料並且重新格式化這些資料提交給使用者,或者以固定的格式來儲存這些資料。幸運的是,Guava提供給我們非常好用的類使我們操作字串更加輕鬆。這些類如下:
- CharMatcher
- Charsets
- Strings
現在我們看一看如何在代碼中使用它們。在第一個例子當中,這個單元測試將會展示使用ASCII類方法來確定一個字元是否是小寫。第二個例子將展示將小寫字串轉換成大寫。
使用Charsets類在java當中,有6個標準字元集在每一個java平台都會被支援。這與經常運行下面代碼是相關的:
byte[] bytes = someString.getBytes();
但是有一個問題關於上面的代碼。沒有指定你想返回位元組的字元集,你將會獲得系統使用運行時預設的字元集返回的位元組,這可能會產生問題有可以預設字元集不是你想要的。所以最佳的做法是像下面這樣:
try{ bytes = "foobarbaz".getBytes("UTF-8");}catch (UnsupportedEncodingException e){ //This really can‘t happen UTF-8 must be supported}但是仍然有兩個問題在上面的代碼當中:
- UTF-8在java平台 一定會被支援,所以UnsupportedEncodingException一定不會被拋出
- 一旦我們使用字串指定字元集的定義,我們可以產生拼字錯誤然後導致異常拋出。
Charsets類可以協助我們,Charsets類提供了static final 的六個java平台支援的字元集。使用Charsets類我們可以使上面的例子更簡單些:
byte[] bytes2 = "foobarbaz".getBytes(Charsets.UTF_8);
值得注意的是在JAVA7當中,StandardCharsets也提供了同樣的功能。現在我們看看Strings類。
使用Strings類Strings類提供一些便利實用的方法處理字串。你是否寫過像下面的代碼:
StringBuilder builder = new StringBuilder("foo"); char c = ‘x‘; for(int i=0; i<3; i++){ builder.append(c); }return builder.toString();上面例子當中的6行代碼我們可以使用一行代碼來替換。
Strings.padEnd("foo",6,‘x‘);第二個參數是很重要的,6指定返回的字串最小長度為6,而不是指定‘x‘字元在字串後面追加多少次。如果提供的字串長度已經大於了6,則不會進行追加。同樣也有一個相類似的padStart方法可以在給定字串的前面追加字元到指定的長度。在Strings類當中有三個非常有用的方法來處理空值的:
- nullToEmpty:這個方法接受一個字串參數,如果傳入的參數不是null值或者長度大於0則原樣返回,否則返回空串("");
- emptyToNull:這個方法類似於nullToEmpty,它將返回null值如果傳入的參數是空串或者null。
- isNullOrEmpty:這個方法會檢查傳入參數是否為null和長度,如果是null和長度為0就返回true。
或許,這將是一個不錯的主意總是在任何使用nullToEmpty方法處理傳入的字串參數。
使用CharMatcher類CharMatcher提供了在一定字串中匹配是否存在特定字串的功能。在CharMatcher類當中的方法也可以讓格式化更加簡單。例如,你可以將多行的字串格式化成一行,並且分行符號將會以空格來代替。
CharMatcher.BREAKING_WHITESPACE.replaceFrom(stringWithLinebreaks,‘ ‘);
還有一個版本replaceFrom的,需要一個CharSequence的值作為第2個參數值,而不是一個單一的字元。移除多個空格和tab以單個空格來代替,代碼如下:
@Testpublic void testRemoveWhiteSpace(){ String tabsAndSpaces = "String with spaces and tabs"; String expected = "String with spaces and tabs"; String scrubbed = CharMatcher.WHITESPACE.collapseFrom(tabsAndSpaces,‘ ‘); assertThat(scrubbed,is(expected));}在上面的測試代碼中,我們把所有多個空格和tab都替換成了一個空格,所有都在一行上。上面例子在某些情況下可行,但是如果字串在開頭就有空格我們想移除怎麼辦?返回的字串當中前面依然會包含空格,這時可以使用trimAndCollapseFrom方法:
@Testpublic void testTrimRemoveWhiteSpace(){ String tabsAndSpaces = " String with spaces and tabs"; String expected = "String with spaces and tabs"; String scrubbed = CharMatcher.WHITESPACE. trimAndCollapseFrom(tabsAndSpaces,‘ ‘); assertThat(scrubbed,is(expected));}在這個測試當中,我們再一次將把多個空格和tab替換成一個空格也在一行上。列出所有在CharMatcher類可用的方法是不切實際的,這裡是不是更換一組匹配字元的地方,我們保留所匹配的字元的例子:
@Test public void retainFromTest() { String lettersAndNumbers = "foo989yxbar234"; String expected = "989234"; String actual = CharMatcher.JAVA_DIGIT.retainFrom(lettersAndNumbers); assertEquals(expected, actual); }在這個例子當中我們找到"foo989yxbar234"字串當中所以的數字並且保留下來。往下繼續之前,我們應該看看最後一個CharMatcher類中的強大功能。可以聯合多個CharMatcher類執行個體建立一個新的CharMatcher類執行個體。假設你需要創一個匹配數字和空格的CharMatcher類執行個體:
CharMatcher cm = CharMatcher.JAVA_DIGIT.or(CharMatcher.WHITESPACE);
它現在將會匹配任何數字或者空格。CharMatcher類對於操作字串來說功能強大並且很好用。
使用Preconditions類Preconditions類是用來驗證我們的代碼狀態的靜態方法的集合。 Preconditions非常重要,因為他們保證我們的期望執行成功的代碼得到滿足。 如果條件與我們期望的不同,我們會及時反饋問題。和以前一樣,使用前提條件的重要性是確保我們代碼的行為,並在調試中很有用。你可以寫你自己的先決條件,像下面這樣:
if(someObj == null){ throw new IllegalArgumentException(" someObj must not be null"); }使用Preconditions當中的方法(需要靜態匯入),檢查一個空值更簡單。
checkNotNull(someObj,"someObj must not be null");
接下來,我們將要展示使用先決條件的例子:
public class PreconditionExample { private String label; private int[] values = new int[5]; private int currentIndex; public PreconditionExample(String label) { //返回label如果不為空白 this.label = checkNotNull(label,"Label can‘‘t be null"); } public void updateCurrentIndexValue(int index, int valueToSet) { //檢查索引是否有效 this.currentIndex = checkElementIndex(index, values.length, "Index out of bounds for values"); //檢查參數值 checkArgument(valueToSet <= 100,"Value can‘t be more than 100"); values[this.currentIndex] = valueToSet; } public void doOperation(){ checkState(validateObjectState(),"Can‘t perform operation"); } private boolean validateObjectState(){ return this.label.equalsIgnoreCase("open") && values[this. currentIndex]==10; }}下面是對上面例子當中四個方法的摘要資訊:
- checkNotNull(T object, Object message):這個方法如果object不為null直接返回,如果為null會拋出null 指標異常。
- checkElementIndex (int index, int size, Object message):在這方法當中,index是你將要訪問的元素下標,size是這個要訪問的array,list或者字串的長度。然後校正是否有效,如果無效拋出IndexOutOfBoundsException。
- checkArgument (Boolean expression, Object message):這方法傳入布林運算式。 這個布林運算式如果為true則繼續執行,否則拋出IllegalArgumentException。
- checkState (Boolean expression, Object message):這方法傳入 一個布林運算式涉及對象的狀態,而不是參數。 這個布林運算式如果為true則繼續執行,否則拋出IllegalArgumentException。
Object工具在這個章節當中我們將介紹協助檢查null值和建立toString和hashCode的方法的實用方法。我們接著去看看一個有用的類,它實現Comparable介面。
Getting help with the toString method當我們要調試的時候,toString方法是必須重寫的,重寫它的乏味無趣的。然而,Objects類可以使用toStringHelper方法讓重寫更簡單。看下面的例子:
public class Book implements Comparable<Book> { private Person author; private String title; private String publisher; private String isbn; private double price; public String toString() { return Objects.toStringHelper(this).omitNullValues().add("title", title).add("author", author).add("publisher", publisher) .add("price",price).add("isbn", isbn).toString(); }}讓我們看看toString方法:
- 首先我們傳入一個Book對象來建立一個Objects.ToStringHelper執行個體。
- 第二步,我們調用omitNullValues來排除任何null值的屬性。
- 調用add方法來添加每一個屬性的標籤和屬性。
檢查null
值firstNonNull方法接受2個參數並且返回第一個參數如果它不為null。
String value = Objects.firstNonNull(someString, "default value");
firstNonNull方法使用時候如果你不確定傳入的值是否為null你可以提供一個預設值給它。需要注意:如果傳入的2個參數都是null,會拋出null 指標異常。
建立hash codes為類寫hashCode方法是基本的但是乏味無趣。Objects類可以協助我們使用hashCode方法更加簡單。考慮下Book類有4個屬性:title, author, publisher, 和isbn. 下面的代碼將展示使用Object.hashCode方法返回hashCode值。
public int hashCode() { return Objects.hashCode(title, author, publisher, isbn);}
實現CompareTo方法再次使用Book類,下面是典型的實現compareTo方法。public int compareTo(Book o) {
int result = this.title.compareTo(o.getTitle()); if (result != 0) { return result; } result = this.author.compareTo(o.getAuthor()); if (result != 0) { return result; } result = this.publisher.compareTo(o.getPublisher()); if(result !=0 ) { return result; } return this.isbn.compareTo(o.getIsbn());}現在讓我們看一看使用ComparisonChain類來實現compareTo方法:
public int compareTo(Book o) { return ComparisonChain.start() .compare(this.title, o.getTitle()) .compare(this.author, o.getAuthor()) .compare(this.publisher, o.getPublisher()) .compare(this.isbn, o.getIsbn()) .compare(this.price, o.getPrice()) .result(); }第二個例子顯得更緊湊和更好閱讀。而且,ComparisonChain類會在第一個比較當中如果返回非零時停止比較,只有一種情況返回0,那就是所有的比較返回的都是0。
概要在本章中我們介紹了許多基礎操作。我們學習了如何使用Guava讓分割字串更簡單通過使用Joiner, Splitter,和非常有用的MapJoiner和MapSplitter類。我們也學習了使用Charsets, CharMatcher, 和 Strings 來操作字串。我們學習了如何讓我們代碼更加強壯和便於調試通過使用Preconditions類。在Objects類中,我們學習了一些使用方法來設定預設值和建立toString和hashCode方法。我們也學習了使用ComparisonChain類來實現compareTo方法更簡單。在下一章,我們學習如何使用Guava來進行函數式編程,通過Function和Predicate介面使我們的編程效率更加高。
[轉]基本Guava工具