5個 Google Guava 鮮為人知的特性

Java譯站發表於2015-03-27

Google Guava有哪些比較冷門但卻又實用的特性呢?

它是最流行的開源庫之一,你應該聽過它的大名,它誕生的地方正是人們舉辦真正的魁地奇比賽的地方(起碼實習期內是這樣的)。它雖然不是來自哈利波特中的霍格沃茲學校,但卻有著自己的專屬魔力:Google Guava庫包含著許多誕生於Google的核心Java庫,這些都是公開發布後在生產環境經歷過了各種檢驗的。在Java 8之前它就已經包含Optional了。

image

Guava致力於提升常見任務的開發效率,通過它所提供的功能,開發人員能夠高效地完成更優質且更乾淨的程式碼。最著名的莫過於它裡面的集合庫和快取庫了。然而,它的很多非常實用的功能卻鮮為人知。說到集合和快取,Guava庫對JDK中的集合API進行了改進,同時它還填補了直到去年才最終釋出的JCache的空缺(令人望眼欲穿)。本文我和大家分享的是在Takipi裡面我們所用到的一些Google Guava的特性,以及我們的一些有趣的發現。

注意:Guava支援Java 6及以上的版本。

1. 無符號基礎型別:它們真的存在!

Java 8中一個不太為人所知的特性就是它為無符號基礎型別所提供的新的解決方案。但更加不為人所知的是在Java 8釋出很久之前Guava庫就已經有了這個功能了,目前在Java 6及以後的版本中均能使用。我們來看下Guava是如何解決這個問題的。現在在我們面前有兩種選擇,到底使用哪種最好保持一致:

直接將基礎型別當int來使用,但要記清楚它可是無符號的:

int notReallyInt = UnsignedInts.parseUnsignedInt(4294967295); // Max unsigned int

String maxUnsigned = UnsignedInts.toString(notReallyInt); // We’re legit!

UnsignedInts與UnsignedLongs還支援compare, divide, min, max等方法。

你還可以使用包裝型別,這樣能避免直接使用基礎型別容易帶來的混淆:

UnsignedInteger newType = UnsignedInteger.valueOf(maxUnsigned);

newType = newType.plus(UnsignedInteger.valueOf("1")); // Increment

UnsignedInts與UnsignedLongs還支援minus, times, dividedBy以及mod方法。

2. 雜湊:128位的MurmurHash

看一下Java標準庫中的非加密雜湊演算法你會發現少了MurmurHash,這是一個簡單高效且還是分散式的演算法,在許多語言中都有著很好的支援。我們並不是說要用它來取代Java的hashCode方法,不過如果你想要生成大量的雜湊值而32位已經不夠用了,但又希望能有一個高效而不會影響到效能的演算法,那肯定就是它了。下面是Guava中的實現:

HashFunction hf = Hashing.murmur3_128(); // 32bit version available as well
HashCode hc = hf.newHasher()
   .putLong(id)
   .putString(name, Charsets.UTF_8)
   .putObject(person, personFunnel)
   .hash();

你可以使用Funnel來對物件進行分解,裡面包含了用於讀取物件的指令,假設我們有一個帶ID,名字以及出生年份的Person物件:

Funnel<Person> personFunnel = new Funnel<Person>() {
   @Override
   public void funnel(Person person, PrimitiveSink into) {
   into
      .putInt(person.id)
      .putString(person.firstName, Charsets.UTF_8)
      .putString(person.lastName, Charsets.UTF_8)
      .putInt(birthYear);
   }
};

3. InternetDomainName:用它來取代你的域名校驗器

Guava還有一個很酷的功能就是它的InternetDomainName,用它來解析及修改域名簡直是得心應手。如果你自己寫過類似的功能的話,你就會知道它提供的方式是多高效優雅了。它是Mozilla基金會發起的專案,遵循最新的RFC規範,它採用的是公共字尾列表(Public Suffix List, PSL)中的域名列表。與apache-common庫中的競爭者相比,它還提供了許多專門的方法。我們來看一個簡單的例子:

InternetDomainName owner =
   InternetDomainName.from("blog.takipi.com").topPrivateDomain(); // returns takipi.com

InternetDomainName.isValid(“takipi.monsters"); // returns false

關於域名有幾個概念是比較容易混淆的:publicSuffix()返回的是對應著公共字尾列表中的獨立實體的頂級域名。因此返回的可能會有co.uk, .com, .cool這樣的結果(沒錯,.cool是一個真實的字尾,比如javais.cool, scalais.cool以及cppis.cool)。而topPrivateDomain(),這是對應公共字尾列表的一個獨立實體的私有域名。在blog.takipi.com上呼叫這個方法會返回takipi.com,但如果你把它用於某個github主頁,比如username.github.io的話則會返回username.github.io,因為這在PSL上是一個單獨的實體。

當你需要校驗域名的時候這個功能就派上用場了,比如我們最近給將JIRA整合進Takipi的時候,首先我們要檢查你的JIRA域名,然後才能連線到Takipi的生產環境的錯誤分析工具中。

4. ClassPath反射:魔鏡,魔鏡

看一下Java的反射機制,也就是它的檢視自身程式碼的能力,你會發現,要想列出所在包或者專案中的所有類可不是一件簡單的事情。這是Guava中我們非常喜歡的一個特性,它還能獲取當前執行環境的許多相關資訊。使用起來非常簡單:

ClassPath classpath = ClassPath.from(classloader);
for (ClassPath.ClassInfo classInfo : classpath.getTopLevelClasses("com.mycomp.mypackage")) {
   System.out.println(classInfo.getName());
}

這段程式碼會遍歷你指定包中的所有類並列印出它們的名字。這裡要說明的是它只會掃描我們指定的包的物理路徑下的類。如果類是從其它地方載入進來的則不在此列,因此使用它的時候請務必小心,不然你得到的結果就是錯誤的了。

5: CharMatcher:簡化版正則?

我們用一個你肯定會碰到過的問題來結束這最後一個特性。假設你有一個字串,或者許多字串,你希望對它們進行格式化,比如刪除空格或者別的字元,替換某個字元等等。總的來說,就是提取匹配某個模式的字元然後進行某個操作。Guava提供了CharMatcher,使得這類問題的處理更得更加優雅。

對於這類任務,庫裡有許多預定義好的模式,比如JAVAUPPERCASE(大寫字元),JAVA_DIGIT(數字),INVISIBLE(不可見UNICODE字元)等。除了這些預定義的模式外,你還可以建立自己想要的模式。我們用一段簡短的示例來看下它是如何使用的:

String spaced = CharMatcher.WHITESPACE.trimAndCollapseFrom(string, ‘ ‘);

它會擷取掉字串末尾的空格並將中間連續的空格合併成一個。

String keepAlex = CharMatcher.anyOf(“alex”).retainFrom(someOtherString);

而這行會將一個字串中我的名字裡沒有的字元都去掉。如果我是一名說唱歌手的話,這將是我的歌曲揚名之時。

結論

這裡我們介紹了Google Guava庫中的一些非常有趣的特性,當然了,不包括家喻戶曉的集合庫以及快取庫。這裡面有些功能是在Takipi中廣泛用到的,而有些功能是我們覺得比較實用,相信許多專案都能從中受益的。Google Guava庫讓開發人員變得更加高效,而這也正是我們Takipi所開發的工具想要實現的目標(它可是相當酷的,不信你可以試試

我們很想知道你用過的哪個Guava特性是別人可能不太會用到的?(集合和快取當然不算!)請在下方的評論中分享給大家吧。

相關文章