一、Guava
Strings
package com.google.common.base;
Srings類程序中經常使用。
比如判斷字符串是否為空,我們在之前用jdk方法判斷是會用下面這個判斷語句。
if( input==null || input.equals("") ){
System.out.println("輸入字符串為空");
}
上面的代碼如果不注意的話就容易寫錯,並且不優美。現在采用guava的Strings類進行判斷,請看下面的
if(Strings.isNullOrEmpty(input)){
System.out.println("輸入字符串為空");
}
這樣是不是看起來很優雅。
下面開始閱讀Strings的源碼:
1、nullToEmpty()
public static String nullToEmpty(@Nullable String string) {
return (string == null) ? "" : string;
}
從源碼中可以看到:return (string == null) ? "" : string;
如果字符串對象為空,則返回空字符串"",否則就返回原字符串。
看一下怎么使用的:
Strings.nullToEmpty(null);//""
Strings.nullToEmpty("zhang");//返回"zhang"
2、emptyToNull()
public static String emptyToNull(@Nullable String string) {
return isNullOrEmpty(string) ? null : string;
}
這個方法就與上面的emptyToNull()方法相反了,如果輸入的是空字符串,那么就返回null,否則返回原字符串
Strings.emptyToNull("");//返回null
Strings.emptyToNull("zhang");//返回"zhang"
3、isNullOrEmpty()
public static boolean isNullOrEmpty(@Nullable String string) {
return Platform.stringIsNullOrEmpty(string);
}
Returns {@code true} if the given string is null or is the empty string.
這句話的意思是如果輸入的字符串對象是null或者輸入的字符串內容為空,那么就返回true。
Strings.isNullOrEmpty("");//返回true
Strings.nullToEmpty(null);//""
4、padStart()方法
public static String padStart(String string, int minLength, char padChar) {
checkNotNull(string); // eager for GWT.
if (string.length() >= minLength) {
return string;
}
StringBuilder sb = new StringBuilder(minLength);
for (int i = string.length(); i < minLength; i++) {
sb.append(padChar);
}
sb.append(string);
return sb.toString();
}
從源碼中我們可以看出,輸入參數為為三個,一個是字符串,一個是長度,一個是 字符
結果返回一個長度至少是minLength的字符串,如果string長度不夠就在它前面添加若干個padChar,以使結果字符串長度為minLength。
看一下了例子:
padStart("7", 3, '0');
returns {"007"} padStart("2010", 3, '0');
returns {"2010"}
5、padEnd()
public static String padEnd(String string, int minLength, char padChar) {
checkNotNull(string); // eager for GWT.
if (string.length() >= minLength) {
return string;
}
StringBuilder sb = new StringBuilder(minLength);
sb.append(string);
for (int i = string.length(); i < minLength; i++) {
sb.append(padChar);
}
return sb.toString();
}
那么padEnd方法就與上面的padStart方法相反了。
如果長度不夠,在string后補padChar。
padEnd("4.", 5, '0');
returns {"4.000"}
padEnd("2010", 3, '!');
returns { "2010"}
6、repeat()
public static String repeat(String string, int count) {
checkNotNull(string); // eager for GWT.
if (count <= 1) {
checkArgument(count >= 0, "invalid count: %s", count);
return (count == 0) ? "" : string;
}
// IF YOU MODIFY THE CODE HERE, you must update StringsRepeatBenchmark
final int len = string.length();
final long longSize = (long) len * (long) count;
final int size = (int) longSize;
if (size != longSize) {
throw new ArrayIndexOutOfBoundsException("Required array size too large: " + longSize);
}
final char[] array = new char[size];
string.getChars(0, len, array, 0);
int n;
for (n = len; n < size - n; n <<= 1) {
System.arraycopy(array, 0, array, n, n);
}
System.arraycopy(array, 0, array, n, size - n);
return new String(array);
}
這個方法就是將輸入的字符串重復拼接count次
repeat("hey", 3) ;
returns the string {"heyheyhey"}
7、commonPrefix()
public static String commonPrefix(CharSequence a, CharSequence b) {
checkNotNull(a);
checkNotNull(b);
int maxPrefixLength = Math.min(a.length(), b.length());
int p = 0;
while (p < maxPrefixLength && a.charAt(p) == b.charAt(p)) {
p++;
}
if (validSurrogatePairAt(a, p - 1) || validSurrogatePairAt(b, p - 1)) {
p--;
}
return a.subSequence(0, p).toString();
}
通過源碼我們很好理解這個方法的目的是查詢兩個字符串的最長公共前綴。如果沒有公共前綴的話就返回空字符串。
Strings.commonPrefix("aaab", "aac");//"aa"否則返回""
8、commonSuffix()
public static String commonSuffix(CharSequence a, CharSequence b) {
checkNotNull(a);
checkNotNull(b);
int maxSuffixLength = Math.min(a.length(), b.length());
int s = 0;
while (s < maxSuffixLength && a.charAt(a.length() - s - 1) == b.charAt(b.length() - s - 1)) {
s++;
}
if (validSurrogatePairAt(a, a.length() - s - 1)
|| validSurrogatePairAt(b, b.length() - s - 1)) {
s--;
}
return a.subSequence(a.length() - s, a.length()).toString();
}
該方法返回兩個字符串的最長公共后綴。
Strings.commonSuffix("aaac", "aac");//"aac"否則返回""
Files
package com.google.common.io;
之前讀文件時一直采用類似這種方式:
bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
Files,文件操作類。
readLines(File file, Charset charset),這個方法將File按行存入list<String>中。
return readLines( file, charset, //編碼方式,通常都是utf-8 new LineProcessor<List<String>>() { final List<String> result = Lists.newArrayList(); @Override public boolean processLine(String line) { result.add(line); return true; } @Override public List<String> getResult() { return result; } });
public static <T> T readLines(File file, Charset charset, LineProcessor<T> callback) //這個方法在將文件按行存儲為Lists<String>時,同時調用行處理方法,只有滿足要求的結果才會存到結果中。 throws IOException { return asCharSource(file, charset).readLines(callback); }
public static String toString(File file, Charset charset) throws IOException { //將文件轉化為String,並返回,包含文件中的所有字符,包括換行符 return asCharSource(file, charset).read(); }
public static boolean equal(File file1, File file2) throws IOException //如果這兩個文件相同,則返回true。這時不僅是內容,還包括文件長度。 if (file1 == file2 || file1.equals(file2)) { return true; } if (len1 != 0 && len2 != 0 && len1 != len2) { return false; }
public static void copy(File from, File to) throws IOException { //拷貝一個文件里的所以字符給另一個文件 checkArgument(!from.equals(to), "Source %s and destination %s must be different", from, to); asByteSource(from).copyTo(asByteSink(to)); }
public static void write(CharSequence from, File to, Charset charset) throws IOException { //將指定內容寫入文件,如果文件原本存在內容,則覆蓋 asCharSink(to, charset).write(from); }
public static void append(CharSequence from, File to, Charset charset) throws IOException { //追加文件,將指定內容追加到文件尾 write(from, to, charset, true); }
MultiSet
package com.google.common.collect;
我們在進行字符統計時,同常采用的方法就是:
String[] text=new String[]{"the weather is good ","today is good","today has good weather","good weather is good"};
HashMap<String, Integer> hashMap=new HashMap<String, Integer>();
for (int i=0;i<text.length;i++){
String temp=text[i];
String[] words=temp.split("\\s");
for(int j=0;j<words.length;j++){
if(!hashMap.containsKey(words[j])){
hashMap.put(words[j], new Integer(1));
}else{
int k=hashMap.get(words[j]).intValue()+1;
hashMap.put(words[j], new Integer(k));
}
}
}
這種方法的思想就是:首先建立一個Map,key值存儲單詞,value存儲出現次數,在循環添加單詞,如果沒有相同的key,則將單詞添加到key中,並設置它的value值為1,如果map中含有相同的key,則將對應的value值加1。這種方法冗余且容易出錯。guava設計了一個集合類,Multiset,就是今天我們要介紹的。
先看看Multiset怎么進行詞頻統計的:
String[] text=new String[]{"the weather is good ","today is good","today has good weather","good weather is good"}; Multiset<String> set = HashMultiset.create(list);for (int i=0;i<text.length;i++){ String temp=text[i]; String[] words=temp.split("\\s"); for(int j=0;j<words.length;j++){ set.add(words[j]); } } 在獲取某個單詞的個數時: System.out.println(set.count("the")); //這樣就可以了哦
簡單吧,Mutiset解決了我們很多問題,從類名上我們就可以知道這個set集合可以存放相同的元素。
現在看看它的主要用法:
Multiset接口定義的接口主要有:
add(E element) :向其中添加單個元素
add(E element,int occurrences) : 向其中添加指定個數的元素 count(Object element) : 返回給定參數元素的個數 remove(E element) : 移除一個元素,其count值 會響應減少 remove(E element,int occurrences): 移除相應個數的元素 elementSet() : 將不同的元素放入一個Set中 entrySet(): 類似與Map.entrySet 返回Set<Multiset.Entry>。包含的Entry支持使用getElement()和getCount() setCount(E element ,int count): 設定某一個元素的重復次數 setCount(E element,int oldCount,int newCount): 將符合原有重復個數的元素修改為新的重復次數 retainAll(Collection c) : 保留出現在給定集合參數的所有的元素 removeAll(Collectionc) : 去除出現給給定集合參數的所有的元素
實例:
Multiset<String> wordsMultiset = HashMultiset.create(); wordsMultiset.addAll(wordList); for(String key:wordsMultiset.elementSet()){ System.out.println(key+" count:"+wordsMultiset.count(key)); } if(!wordsMultiset.contains("peida")){ wordsMultiset.add("peida", 2); } for(String key:wordsMultiset.elementSet()){ System.out.println(key+" count:"+wordsMultiset.count(key)); } if(wordsMultiset.contains("peida")){ wordsMultiset.setCount("peida", 23); } System.out.println("============================================"); for(String key:wordsMultiset.elementSet()){ System.out.println(key+" count:"+wordsMultiset.count(key)); } if(wordsMultiset.contains("peida")){ wordsMultiset.setCount("peida", 23,45); } System.out.println("============================================"); for(String key:wordsMultiset.elementSet()){ System.out.println(key+" count:"+wordsMultiset.count(key)); } if(wordsMultiset.contains("peida")){ wordsMultiset.setCount("peida", 44,67); } System.out.println("============================================"); for(String key:wordsMultiset.elementSet()){ System.out.println(key+" count:"+wordsMultiset.count(key)); }
二、java8
上面帶大家熟悉一下guava工具包。
現在主要介紹下常用的java8。
我們程序里用到了很多的lamada表達式,那么什么是lamada表達式呢?
一段帶有輸入參數的可執行語句塊。
比如:
List<String> names = Lists.newArrayList("apple","pear","banana"); Collections.sort(names,((o1, o2) -> o1.compareTo(o2)));
如果不用lambda表達式,應該怎么樣編寫呢?
List<String> names = Lists.newArrayList("apple","pear","banana"); Collections.sort(names, new Comparator<String>() { @Override public int compare(String o1, String o2) { return o1.compareTo(o2); } });
可以明顯的看出lambda表達式的優點。
現在有一個題目:將字符串類型的列表里的元素全部轉換成大寫。
如果采用原始的方法:
List<String> list = new ArrayList<String>(); list.add("I am a boy"); list.add("I love the girl"); list.add("But the girl loves another girl"); List<String> upperCaseList = new ArrayList<String>(); for(String name : list){ upperCaseList.add(name.toUpperCase()); }
采用guava:
List<String> upcaseNames = FluentIterable.from(list).transform(new Function<String, String>() { @Override public String apply(String input) { return input.toUpperCase(); } }).toList();
假如使用lambda:
List<String> upcaseNames = list.stream().map(String::toUpperCase).collect(Collectors.toList());
我們在此抽象一下lambda表達式的一般語法:
(Type1 param1, Type2 param2, ..., TypeN paramN) -> { statment1; statment2; //............. return statmentM; }
從lambda表達式的一般語法可以看出來,lambda確實是“一段帶有輸入參數的可執行語句塊”。
1. 參數類型省略–絕大多數情況,編譯器都可以從上下文環境中推斷出lambda表達式的參數類型。這樣lambda表達式就變成了:
(param1,param2, ..., paramN) -> { statment1; statment2; //............. return statmentM; }
比如:
List<String> upperCaseNames = names.stream().map((name) -> {return name.toUpperCase();}).collect(Collectors.toList());
2、當lambda表達式的參數個數只有一個,可以省略小括號。lambda表達式簡寫為:
param1 -> { statment1; statment2; //............. return statmentM; }
比如:List<String>
upperCaseNames= names.stream().map(name -> {
return
name.toUpperCase();}).collect(Collectors.toList());
3、 當lambda表達式只包含一條語句時,可以省略大括號、return和語句結尾的分號。lambda表達式簡化為:param1 -> statment
比如:
List<String> upperCaseNames = names.stream().map(name -> name.toUpperCase
()).collect(Collectors.toList());
在這里需要說明一下,從上面看到lambda表達一直是接收內部變量,那么是否可以接收外部參數呢?
我們嘗試了一下:

發現有錯誤。提示說lamada表達式只能接收不可變的參數。
那么這是為什么呢?
因為lambda表達式是內部類。
內部類里面使用外部類的局部變量時,其實就是內部類的對象在使用它,內部類對象生命周期中都可能調用它,
而內部類試圖訪問外部方法中的局部變量時,外部方法的局部變量很可能已經不存在了,那么就得延續其生命
,拷貝到內部類中,而拷貝會帶來不一致性,從而需要使用final聲明保證一致性。
上面在介紹lambda時,會發現使用了很多stream的東西。
那么什么是stream(流)。
Stream是元素的集合,這點讓Stream看起來用些類似Iterator; 可以支持順序和並行的對原Stream進行匯聚的操作;
大家可以把Stream當成一個高級版本的Iterator。原始版本的Iterator,用戶只能一個一個的遍歷元素並對其執行某些操作;高級版本的Stream,用戶只要給出需要對其包含的元素執行什么操作,比如“過濾掉長度大於10的字符串”、“獲取每個字符串的首字母”等。
//Lists是Guava中的一個工具類 List<Integer> nums = Lists.newArrayList(1,null,3,4,null,6); nums.stream().filter(num -> num != null).count();
看上面這段代碼

在此我們總結一下使用Stream的基本步驟:
- 創建Stream;
- 轉換Stream,每次轉換原有Stream對象不改變,返回一個新的Stream對象(**可以有多次轉換**);
- 對Stream進行聚合(Reduce)操作,獲取想要的結果;
分析好stream的處理流程后,看看stream可以進行哪些操作:
1. distinct: 對於Stream中包含的元素進行去重操作(去重邏輯依賴元素的equals方法),新生成的Stream中沒有重復的元素;
2. filter: 對於Stream中包含的元素使用給定的過濾函數進行過濾操作,新生成的Stream只包含符合條件的元素;
filter方法示意圖:
3. map: 對於Stream中包含的元素使用給定的轉換函數進行轉換操作,新生成的Stream只包含轉換生成的元素。這個方法有三個對於原始類型的變種方法,分別是:mapToInt,mapToLong和mapToDouble。這三個方法也比較好理解,比如mapToInt就是把原始Stream轉換成一個新的Stream,這個新生成的Stream中的元素都是int類型。之所以會有這樣三個變種方法,可以免除自動裝箱/拆箱的額外消耗;
map方法示意圖:
4. flatMap:和map類似,不同的是其每個元素轉換得到的是Stream對象,會把子Stream中的元素壓縮到父集合中;
flatMap方法示意圖:
5. peek: 生成一個包含原Stream的所有元素的新Stream,觀察流的內容,返回原來的流保持不變,action用來觀察流中的每個元素,該方法主要用來調試,並不產生實際作用
peek方法示意圖:
6. limit: 對一個Stream進行截斷操作,獲取其前N個元素,如果原Stream中包含的元素個數小於N,那就獲取其所有的元素;
limit方法示意圖:
7. skip: 返回一個丟棄原Stream的前N個元素后剩下元素組成的新Stream,如果原Stream中包含的元素個數小於N,那么返回空Stream;
skip方法示意圖:
8. 上面的操作組合在一起:
List<Integer> nums = Lists.newArrayList(1,1,null,2,3,4,null,5,6,7,8,9,10); System.out.println(“sum is:”+nums.stream().filter(num -> num != null). distinct().mapToInt(num -> num * 2). peek(System.out::println).skip(2).limit(4).sum());
當然,進行任何操作,最終都是想得到一個結果,stream同樣提供了很多聚合操作,主要分為兩部分:
- 可變匯聚:把輸入的元素們累積到一個可變的容器中,比如Collection或者StringBuilder;
- 其他匯聚:除去可變匯聚剩下的,一般都不是通過反復修改某個可變對象,而是通過把前一次的匯聚結果當成下一次的入參,反復如此。比如reduce,count,allMatch;
比如:
list.stream().map(String::toUpperCase).collect(Collectors.toList());
List<Integer> ints = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10);
System.out.println("ints sum is:" + ints.stream().reduce(0, (sum, item) -> sum + item));
List<Integer> ints = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10); System.out.println("ints sum is:" + ints.stream().count());
List<Integer> ints = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10); System.out.println(ints.stream().allMatch(item -> item < 100)); ints.stream().max((o1, o2) -> o1.compareTo(o2)).ifPresent(System.out::println);