Ordering 是一个特殊的Comparator 实例。然而由于其良好的扩展性,可轻轻松构造复杂的comparator,比如加强型的容器的比较、排序等等;同时,Ordering提供链式方法调用,使用代码非常简洁漂亮。
Ordering<String> ordering = Ordering.from(String.CASE_INSENSITIVE_ORDER);
ordering.compare("A", "a")
链式比较
Ordering<String> ordering = Ordering.natural().nullsFirst().reverse().lexicographical().reverse().nullsLast();
ordering.sortedCopy(list);
Collection排序比较
Ordering<String> ordering = = Ordering.nullsFirst().reverse();
Collections.sort(names, ordering);
Ordering 详解
创建排序器
方法 | 描述 |
---|---|
natural() | 对可排序类型做自然排序,如数字按大小,日期按先后排序 |
usingToString() | 按对象的字符串形式做字典排序[lexicographical ordering] |
from(Comparator) | 把给定的Comparator转化为排序器 |
链式调用方法
方法 | 描述 |
---|---|
reverse() | 获取语义相反的排序器 |
nullsFirst() | 使用当前排序器,但额外把null值排到最前面 |
nullsLast() | 使用当前排序器,但额外把null值排到最后面 |
compound(Comparator) | 合成另一个比较器,以处理当前排序器中的相等情况 |
lexicographical() | 基于处理类型T的排序器,返回该类型的可迭代对象Iterable<T>的排序器 |
onResultOf(Function) | 对集合中元素调用Function,再按返回值用当前排序器排序 |
运用排序器
方法 | 描述 |
---|---|
greatestOf(Iterable iterable, int k) | 获取可迭代对象中最大的k个元素 |
isOrdered(Iterable) | 判断可迭代对象是否已按排序器排序:允许有排序值相等的元素 |
sortedCopy(Iterable) | 判断可迭代对象是否已严格按排序器排序:不允许排序值相等的元素 |
min(E, E) | 返回两个参数中最小的那个。如果相等,则返回第一个参数 |
min(E, E, E, E...) | 返回多个参数中最小的那个。如果有超过一个参数都最小,则返回第一个最小的参数 |
min(Iterable) | 返回迭代器中最小的元素。如果可迭代对象中没有元素,则抛出NoSuchElementException |
链式简洁风的实现原理
- 基类
基类采用上述静态函数得到实例后,调用自身各种排序算法的接口获取新的排序实例,而接口中采用new子类实例获取,示例如下:
public abstract class Ordering<T> implements Comparator<T> {
public <S extends T> Ordering<S> reverse() {
return new ReverseOrdering<S>(this);
}
......
}
- 子类
子类负责特定的排序算法,示例如下:
final class ReverseOrdering<T> extends Ordering<T> implements Serializable {
ReverseOrdering(Ordering<? super T> forwardOrder) {
this.forwardOrder = checkNotNull(forwardOrder);
}
......
}
总结
Guava的Ordering非常好的扩展性,较易扩展为复杂业务的比较器,且提供流行的链式风格,写代码如写文章,读代码如读美文.
Guava overflow detection
public static long checkedAdd(long a, long b) {
long result = a + b;
checkNoOverflow((a ^ b) < 0 | (a ^ result) >= 0); // if false, throw exception
return result;
}
// on overflow in case 1: a,b same sign, 2: a, b different sign, a and result same sign
public static long checkedSubtract(long a, long b) {
long result = a - b;
checkNoOverflow((a ^ b) >= 0 | (a ^ result) >= 0);
return result;
}
public static short checkedCast(long value) {
short result = (short) value;
checkArgument(result == value, "Out of range: %s", value);
return result;
}
Upcasting
https://www.securecoding.cert.org/confluence/display/java/NUM00-J.+Detect+or+prevent+integer+overflow
public static int multAccum(int oldAcc, int newVal, int scale)
throws ArithmeticException {
final long res = intRangeCheck(
((long) oldAcc) + intRangeCheck((long) newVal * (long) scale)
);
return (int) res; // safe down-cast
}
RangesMatcher
public static final CharMatcher DIGIT = new RangesMatcher(
"CharMatcher.DIGIT", ZEROES.toCharArray(), NINES.toCharArray());
public boolean matches(char c) {
int index = Arrays.binarySearch(rangeStarts, c);
if (index >= 0) {
return true;
} else {
index = ~index - 1;
return index >= 0 && c <= rangeEnds[index];
}
}
Use bitset
public static final CharMatcher JAVA_ISO_CONTROL =inRange('\u0000', '\u001f').or(inRange('\u007f', '\u009f'))
static CharMatcher inRange(final char startInclusive, final char endInclusive,
String description) {
return new FastMatcher(description) {
@Override public boolean matches(char c) {
return startInclusive <= c && c <= endInclusive;
}
@GwtIncompatible("java.util.BitSet")
@Override void setBits(BitSet table) {
table.set(startInclusive, endInclusive + 1);
}
};
}