有時候咱們不可避免地要實現Comparator, 好作排序之類的事情.
要比較兩個整數的時候, 我一度曾經這麼寫:
return a - b;
多簡單啊! 若是a比b大, 無疑這個東西返回正數了.
惋惜啊, 現實永遠比理想殘酷. java的整數不是數學中的整數, 它可能溢出地!
int a = -2000000000;
int b = 2000000000;
System.out.println(a - b);
// prints "294967296"
正確的寫法是:
if (a > b) {
return 1;
} else if (a < b) {
return -1;
} else {
return 0;
}
可是, 太麻煩了哇! 好吧, 好吧, 我知道java是一門羅唆的語言藝術, 講究如何如何啥的, 但是, 但是, 太麻煩了哇! 太麻煩了哇!
在guava裏, 對全部原始類型都提供了比較的工具函數來避免這個麻煩. 好比對long, 能夠用Longs.compare():
return Longs.compare(a, b);
其它, 天然還有Ints, Shorts, Floats, Doubles等等, 就不騙字數了.
下面看一個簡單的model類:
class Person {
final String firstName;
final String lastName;
final int age;
}
下面我來實現一個Comparator, 按照名字而後年齡排序:
class PersonComparator implements Comparator<Person> {
@Override public int compare(Person p1, Person p2) {
int result = p1.firstName.compareTo(p2.firstName);
if (result != 0) {
return result;
}
result = p1.lastName.compareTo(p2.lastName);
if (result != 0) {
return result;
}
return Ints.compare(p1.age, p2.age);
}
}
算中規中矩吧? 嗯, 就是以爲有點羅唆 (好啦, 好啦, "java是一門羅唆的語言藝術", 你好羅唆啊!). 要是能直接就說: 按firstName, lastName, age比較就行了.
有一種作法是把這些東西存到一個List<Comparable>而後用一個Comparator<List>來比較:
Ordering.natural().lexicographical().compare(
Arrays.asList(p1.firstName, p1.lastName, p1.age),
Arrays.asList(p2.firstName, p2.lastName, p2.age));
可是這個東西有點步驟過多, 並且, 自動box那個int, 以及建立兩個臨時List對象, 都彷佛有點過了, 畢竟, Comparator每每是被調用屢次來排序不少對象的.
對此, guava有一個至關聰明的解決辦法, 用
ComparisonChain:
class PersonComparator implements Comparator<Person> {
@Override public int compare(Person p1, Person p2) {
return ComparisonChain.start()
.compare(p1.firstName, p2.firstName)
.compare(p1.lastName, p2.lastName)
.compare(p1.age, p2.age)
.result();
}
}
這個東西的原理哪, 就是利用多態, 當p1.firstName比p2.firstName大的時候, 後續的compare()函數都是空的, 直接返回, 儘可能節省計算.
另外, 由於它對全部原始類型都作了重載, 因此也不會付裝箱的代價.
(我的意見, 不表明組織承認: 這個start()函數有點彆扭. ComparisonChain應該提供靜態compare()方法, 這樣客戶端就能夠省去那個討厭的start())
對了, 剛纔在例子中我實在忍不住引用了
Ordering類. 要說這個類不是作了多少了不起的事情, 它的好處是相關的功能都在一個類裏面, 好找 (點一下ctrl-space, IDE的自動提示就夠用了). 比較經常使用的幾個函數:
- natural(): 比較兩個Comparable.
- reverse(): 把當前ordering反過來, 大的變小, 小的變大.
- compound(): 若是當前ordering比較結果是平局, 用另一個Comparator作加時賽.
- nullsFirst(): 把null看成最小的, 排在前面.
- nullsLast(): null最大.
- binarySearch(): 根據當前ordering在排序列表裏二分查找.
好比, 上面若是我lastName可能爲null, 而後我要把null列到後面, 我就能夠寫:
class PersonOrdering extends Ordering<Person> {
@Override public int compare(Person p1, Person p2) {
return ComparisonChain.start()
.compare(p1.firstName, p2.firstName)
.compare(p1.lastName, p2.lastName, Ordering.<Person>natural().nullsLast())
.compare(p1.age, p2.age)
.result();
}
}
這裏, 既然我已經用Ordering了, 我就順手牽羊把PersonComparator變成PersonOrdering了.