Guava,拯救垃圾代碼,效率提升N倍
01、前世今生
你好呀,我是 Guava。
我由 Google 公司開源,目前在 GitHub 上已經(jīng)有 39.9k 的鐵粉了,由此可以證明我的受歡迎程度。

我的身體里主要包含有這些常用的模塊:集合 [collections] 、緩存 [caching] 、原生類型支持 [primitives support] 、并發(fā)庫 [concurrency libraries] 、通用注解 [common annotations] 、字符串處理 [string processing] 、I/O 等。新版的 JDK 中已經(jīng)直接把我引入了,可想而知我有多優(yōu)秀,忍不住驕傲了。
這么說吧,學(xué)好如何使用我,能讓你在編程中變得更快樂,寫出更優(yōu)雅的代碼!
PS:star 這種事,只能求,不求沒效果??????。二哥開源的《Java 程序員進(jìn)階之路》專欄在 GitHub 上已經(jīng)收獲了 595 枚星標(biāo),鐵粉們趕緊去點點啦,幫二哥沖 600 star,筆芯!
https://github.com/itwanger/toBeBetterJavaer
02、引入 Guava
如果你要在 Maven 項目使用我的話,需要先在 pom.xml 文件中引入我的依賴。
????com.google.guava
????guava
????30.1-jre
一點要求,JDK 版本需要在 8 以上。
03、基本工具
Doug Lea,java.util.concurrent 包的作者,曾說過一句話:“null 真糟糕”。Tony Hoare,圖靈獎得主、快速排序算法的作者,當(dāng)然也是 null 的創(chuàng)建者,也曾說過類似的話:“null 的使用,讓我損失了十億美元。”鑒于此,我用 Optional 來表示可能為 null 的對象。

代碼示例如下所示。
Optional?possible?=?Optional.of(5);
possible.isPresent();?//?returns?true
possible.get();?//?returns?5
我大哥 Java 在 JDK 8 中新增了 Optional 類,顯然是從我這借鑒過去的,不過他的和我的有些不同。
我的 Optional 是 abstract 的,意味著我可以有子類對象;我大哥的是 final 的,意味著沒有子類對象。
我的 Optional 實現(xiàn)了 Serializable 接口,可以序列化;我大哥的沒有。
我的一些方法和我大哥的也不盡相同。
使用 Optional 除了賦予 null 語義,增加了可讀性,最大的優(yōu)點在于它是一種傻瓜式的防護(hù)。Optional 迫使你積極思考引用缺失的情況,因為你必須顯式地從 Optional 獲取引用。
除了 Optional 之外,我還提供了:
參數(shù)校驗 常見的 Object 方法,比如說 Objects.equals、Objects.hashCode,JDK 7 引入的 Objects 類提供同樣的方法,當(dāng)然也是從我這借鑒的靈感。 更強(qiáng)大的比較器
04、集合
首先我來說一下,為什么需要不可變集合。
保證線程安全。在并發(fā)程序中,使用不可變集合既保證線程的安全性,也大大地增強(qiáng)了并發(fā)時的效率(跟并發(fā)鎖方式相比)。
如果一個對象不需要支持修改操作,不可變的集合將會節(jié)省空間和時間的開銷。
可以當(dāng)作一個常量來對待,并且集合中的對象在以后也不會被改變。
與 JDK 中提供的不可變集合相比,我提供的 Immutable 才是真正的不可變,我為什么這么說呢?來看下面這個示例。
下面的代碼利用 JDK 的 Collections.unmodifiableList(list) 得到一個不可修改的集合 unmodifiableList。
List?list?=?new?ArrayList();
list.add("雷軍");
list.add("喬布斯");
List?unmodifiableList?=?Collections.unmodifiableList(list);
unmodifiableList.add("馬云");
運行代碼將會出現(xiàn)以下異常:
Exception?in?thread?"main"?java.lang.UnsupportedOperationException
?at?java.base/java.util.Collections$UnmodifiableCollection.add(Collections.java:1060)
?at?com.itwanger.guava.NullTest.main(NullTest.java:29)
很好,執(zhí)行 unmodifiableList.add() 的時候拋出了 UnsupportedOperationException 異常,說明 Collections.unmodifiableList() 返回了一個不可變集合。但真的是這樣嗎?
你可以把 unmodifiableList.add() 換成 list.add()。
List?list?=?new?ArrayList();
list.add("雷軍");
list.add("喬布斯");
List?unmodifiableList?=?Collections.unmodifiableList(list);
list.add("馬云");
再次執(zhí)行的話,程序并沒有報錯,并且你會發(fā)現(xiàn) unmodifiableList 中真的多了一個元素。說明什么呢?
Collections.unmodifiableList(…) 實現(xiàn)的不是真正的不可變集合,當(dāng)原始集合被修改后,不可變集合里面的元素也是跟著發(fā)生變化。
我就不會犯這種錯,來看下面的代碼。
List?stringArrayList?=?Lists.newArrayList("雷軍","喬布斯");
ImmutableList?immutableList?=?ImmutableList.copyOf(stringArrayList);
immutableList.add("馬云");
嘗試 immutableList.add() 的時候會拋出 UnsupportedOperationException。我在源碼中已經(jīng)把 add() 方法廢棄了。
??/**
???*?Guaranteed?to?throw?an?exception?and?leave?the?collection?unmodified.
???*
???*?@throws?UnsupportedOperationException?always
???*?@deprecated?Unsupported?operation.
???*/
??@CanIgnoreReturnValue
??@Deprecated
??@Override
??public?final?boolean?add(E?e)?{
????throw?new?UnsupportedOperationException();
??}
嘗試 stringArrayList.add() 修改原集合的時候 immutableList 并不會因此而發(fā)生改變。
除了不可變集合以外,我還提供了新的集合類型,比如說:
Multiset,可以多次添加相等的元素。當(dāng)把 Multiset 看成普通的 Collection 時,它表現(xiàn)得就像無序的 ArrayList;當(dāng)把 Multiset 看作
Map時,它也提供了符合性能期望的查詢操作。Multimap,可以很容易地把一個鍵映射到多個值。
BiMap,一種特殊的 Map,可以用
inverse()反轉(zhuǎn)BiMap的鍵值映射;保證值是唯一的,因此values()返回 Set 而不是普通的 Collection。
05、字符串處理
字符串表示字符的不可變序列,創(chuàng)建后就不能更改。在我們?nèi)粘5墓ぷ髦校址氖褂梅浅nl繁,熟練的對其操作可以極大的提升我們的工作效率。
我提供了連接器——Joiner,可以用分隔符把字符串序列連接起來。下面的代碼將會返回“雷軍; 喬布斯”,你可以使用 useForNull(String) 方法用某個字符串來替換 null,而不像 skipNulls() 方法那樣直接忽略 null。
Joiner?joiner?=?Joiner.on(";?").skipNulls();
return?joiner.join("雷軍",?null,?"喬布斯");
我還提供了拆分器—— Splitter,可以按照指定的分隔符把字符串序列進(jìn)行拆分。
Splitter.on(',')
????????.trimResults()
????????.omitEmptyStrings()
????????.split("雷軍,喬布斯,,???沉默王二");
06、緩存
緩存在很多場景下都是相當(dāng)有用的。你應(yīng)該知道,檢索一個值的代價很高,尤其是需要不止一次獲取值的時候,就應(yīng)當(dāng)考慮使用緩存。
我提供的 Cache 和 ConcurrentMap 很相似,但也不完全一樣。最基本的區(qū)別是 ConcurrentMap 會一直保存所有添加的元素,直到顯式地移除。相對地,我提供的 Cache 為了限制內(nèi)存占用,通常都設(shè)定為自動回收元素。
如果你愿意消耗一些內(nèi)存空間來提升速度,你能預(yù)料到某些鍵會被查詢一次以上,緩存中存放的數(shù)據(jù)總量不會超出內(nèi)存容量,就可以使用 Cache。
來個示例你感受下吧。
@Test
public?void?testCache()?throws?ExecutionException,?InterruptedException?{
????CacheLoader?cacheLoader?=?new?CacheLoader()?{
????????//?如果找不到元素,會調(diào)用這里
????????@Override
????????public?Animal?load(String?s)?{
????????????return?null;
????????}
????};
????LoadingCache?loadingCache?=?CacheBuilder.newBuilder()
????????.maximumSize(1000)?//?容量
????????.expireAfterWrite(3,?TimeUnit.SECONDS)?//?過期時間
????????.removalListener(new?MyRemovalListener())?//?失效監(jiān)聽器
????????.build(cacheLoader);?//
????loadingCache.put("狗",?new?Animal("旺財",?1));
????loadingCache.put("貓",?new?Animal("湯姆",?3));
????loadingCache.put("狼",?new?Animal("灰太狼",?4));
????loadingCache.invalidate("貓");?//?手動失效
????Animal?animal?=?loadingCache.get("狼");
????System.out.println(animal);
????Thread.sleep(4?*?1000);
????//?狼已經(jīng)自動過去,獲取為?null?值報錯
????System.out.println(loadingCache.get("狼"));
}
/**
?*?緩存移除監(jiān)聽器
?*/
class?MyRemovalListener?implements?RemovalListener<String,?Animal>?{
????@Override
????public?void?onRemoval(RemovalNotification?notification) ?{
????????String?reason?=?String.format("key=%s,value=%s,reason=%s",?notification.getKey(),?notification.getValue(),?notification.getCause());
????????System.out.println(reason);
????}
}
class?Animal?{
????private?String?name;
????private?Integer?age;
????public?Animal(String?name,?Integer?age)?{
????????this.name?=?name;
????????this.age?=?age;
????}
}
CacheLoader 中重寫了 load 方法,這個方法會在查詢緩存沒有命中時被調(diào)用,我這里直接返回了 null,其實這樣會在沒有命中時拋出 CacheLoader returned null for key 異常信息。
MyRemovalListener 作為緩存元素失效時的監(jiān)聽類,在有元素緩存失效時會自動調(diào)用 onRemoval 方法,這里需要注意的是這個方法是同步方法,如果這里耗時較長,會阻塞直到處理完成。
LoadingCache 就是緩存的主要操作對象了,常用的就是其中的 put 和 get 方法了。
07、尾聲
上面介紹了我認(rèn)為最常用的功能,作為 Google 公司開源的 Java 開發(fā)核心庫,個人覺得實用性還是很高的(不然呢?嘿嘿嘿)。引入到你的項目后不僅能快速的實現(xiàn)一些開發(fā)中常用的功能,而且還可以讓代碼更加的優(yōu)雅簡潔。
我覺得適用于每一個 Java 項目,至于其他的一些功能,比如說散列、事件總線、數(shù)學(xué)運算、反射,就等待你去發(fā)掘了。
這是《Java 程序員進(jìn)階之路》專欄的第 70 篇。該專欄風(fēng)趣幽默、通俗易懂,對 Java 愛好者極度友好和舒適??,內(nèi)容包括但不限于 Java 基礎(chǔ)、Java 集合框架、Java IO、Java 并發(fā)編程、Java 虛擬機(jī)、Java 企業(yè)級開發(fā)(SSM、Spring Boot)等核心知識點。
點擊上方名片,發(fā)送消息「03」 就可以獲取最新版《Java 程序員進(jìn)階之路》PDF 版了,讓我們一起成為更好的 Java 工程師吧,沖!

