# 【Guava 教學】（3）高階排序概念的實現

``````class Person implements Comparable<Person> {
private String lastName;
private String firstName;
private int zipCode;

public int compareTo(Person other) {
int cmp = lastName.compareTo(other.lastName);
if (cmp != 0) {
return cmp;
}
cmp = firstName.compareTo(other.firstName);
if (cmp != 0) {
return cmp;
}
return Integer.compare(zipCode, other.zipCode);
}
}``````

``````import com.google.common.collect.ComparisonChain;

class Person implements Comparable<Person> {
private String lastName;
private String firstName;
private int zipCode;

public int compareTo(Person other) {
return ComparisonChain.start()
.compare(lastName, other.lastName)
.compare(firstName, other.firstName)
.compare(zipCode, other.zipCode)
.result();
}
}``````
`ComparisonChain``start``compare` 都會傳回 `ComparisonChain` 實例，在最後 `result` 計算結果時，就如原先 `compareTo` 的實作，會逐一比較要比較的對象，只要它們各自的 `compareTo` 不為 0 時就傳回結果。
Guava 在排序上提供了一些 API ，確實是很好用，不過這不是這篇文章要論述的，這邊要談的是，如何觀察並抽取出程式碼中更高階的抽象概念，像是排序這樣的東西，其實也一直重複著某些模式。上例中的模式就是：
``````int cmp = f1.compareTo(other.f1);
if (cmp != 0) {
return cmp;
}
cmp = f2.compareTo(other.f2);
if (cmp != 0) {
return cmp;
}
cmp = f3.compareTo(other.f3);
if (cmp != 0) {
return cmp;
}
...``````

``````class StringLengthInverseNullFirstComparator implements Comparator<String> {
@Override
public int compare(String s1, String s2) {
if(s1 == s2) {
return 0;
}
if(s1 == null) {
return -1;
}
if(s2 == null) {
return 1;
}
if(s1.length() == s2.length()) {
return 0;
}
if(s1.length() > s2.length()) {
return -1;
}
return 1;
}
}``````

``````class Natural implements Comparator<Comparable> {
@Override
public int compare(Comparable c1, Comparable c2) {
return c1.compareTo(c2);
}
}

class Inverse implements Comparator {
private Comparator comparator;

public Inverse(Comparator comparator) {
this.comparator = comparator;
}

@Override
public int compare(Object o1, Object o2) {
return -comparator.compare(o1, o2);
}
}

class NullsFirst implements Comparator {
private final static int LEFT_IS_GREATER = 1;
private final static int RIGHT_IS_GREATER = -1;

private Comparator comparator;

public NullsFirst(Comparator comparator) {
this.comparator = comparator;
}

@Override
public int compare(Object o1, Object o2) {
if(o1 == o2) {
return 0;
}
if(o1 == null) {
return RIGHT_IS_GREATER;
}
if(o2 == null) {
return LEFT_IS_GREATER;
}
return comparator.compare(o1, o2);
}
}``````
`Natural``Inverse``NullsFirst` 都是從過去排序經驗中，不斷重現的流程模式中抽取出來的比較器，這樣一來，先前那個 `StringLengthInverseNullFirstComparator` 就可以基於它們來建構了：
``````interface F1<P, R> {
R apply(P p);
}

class StringLengthInverseNullFirstComparator implements Comparator<String> {
private Comparator comparator = new NullsFirst(new Inverse(new Natural()));
private F1<String, Integer> f = new F1<String, Integer>() {
@Override
public Integer apply(String p) {
return p == null ? null : p.length();
}
};
@Override
public int compare(String s1, String s2) {
return comparator.compare(f.apply(s1), f.apply(s2));
}
}``````

``````class OnResultOf<P, R> implements Comparator<P> {
private Comparator comparator;
private F1<P, R> f;

public OnResultOf(F1<P, R> f, Comparator comparator) {
this.f = f;
this.comparator = comparator;
}

@Override
public int compare(P p1, P p2) {
return comparator.compare(f.apply(p1), f.apply(p2));
}
}``````

``````List names = Arrays.asList("Monica", null, "Justin", null, "Jao");

Collections.sort(names,
new OnResultOf(new F1<String, Integer>() {
@Override
public Integer apply(String p) {
return p == null ? null : p.length();
}},
new NullsFirst(new Inverse(new Natural())))
);``````

``````Collections.sort(names,
Ordering.natural().reverse().nullsFirst()
.onResultOf(new Function<String, Integer>() {
@Override
public Integer apply(String p) {
return p == null ? null : p.length();
}
})
);``````
`Ordering` 本身就是比較器，這看看它的類別定義就知道了：
``public abstract class Ordering<T> extends Object implements Comparator<T>``

``````Collections.sort(names,
Ordering.natural().reverse().nullsFirst()
.onResultOf(p -> p == null ? null : p.length())
);``````

Guava 看來只是個程式庫，但它實際上包括了不少高階觀念，先前的兩篇文章 從避免使用 null 開始命名明確的條件檢查，其實也都是在談這些高階觀念，想善用 Guava，瞭解這些觀念是必要的，不然，只是當個程式庫來使用，就沒辦法用得順手，這樣是有點可惜了。 嗯？`Ordering` 更多的範例？其實看 API 文件應該就夠清楚了，如果你瞭解 `Ordering` 存在的目的的話！網路上搜尋一下 「Guava Ordering」，你可以找到一卡車的資料。好吧！我知道有些人很性急，那麼 google's guava library tutorial part 2: joys of Ordering 這個鏈結有不少簡單易懂的範例。