Comparator接口,用于比较两个数,通过比较的结果,进行排序。参数1<、=、>参数2的时候,分别返回-1、0、1。
那么,问题来了,当返回-1的时候,是升序排列,还是降序排列?
通过一段代码,我们来追踪源码分析一波~
对于下面这个Map,key为数据库字段名,List<String>是该字段的所有历史取值。需求:将每个字段的历史取值,按照取值的长短进行排序,长的在前、短者在后。
Map<String, List<String>> history = new HashMap<>();
如下代码,即可满足上面的要求:(整个代码结合了Stream流式操作、lambda表达式,不会的童鞋可以补一波)
// 历史记录按照取值的长短进行排序,长前短后
historyLocal.forEach((field, historyValues) -> {
historyValues.sort(((o1, o2) -> {
if (o1.length() > o2.length()) {
return -1;
} else if (o1.length() < o2.length()) {
return 1;
}
return 0;
}));
history.put(field, historyValues);
});
结合上面的代码,我们来分析一波排序的底层源码。
摁住Ctrl键,点击上面的箭头->,可以发现,我们跳转到了Comparator接口类。这表明,sort方法内部的lambda表达式,其实是对以下compare()方法的具体实现,制定了排序的规则。
public interface Comparator<T> {
int compare(T o1, T o2);
}
sort()方法则根据compare(T o1, T o2)的返回结果(-1、0、1)进行排序。进入sort()方法,我们可以看到以下代码:
default void sort(Comparator<? super E> c) {
Object[] a = this.toArray();
Arrays.sort(a, (Comparator) c);
ListIterator<E> i = this.listIterator();
for (Object e : a) {
i.next();
i.set((E) e);
}
}
这是List接口的默认方法(不清楚默认方法的,可以百度一波哈),从代码可以看出,排序操作主要由Arrays.sort(a, (Comparator) c)实现。进入方法内部,有:
public static <T> void sort(T[] a, Comparator<? super T> c) {
if (c == null) {
sort(a);
} else {
if (LegacyMergeSort.userRequested)
legacyMergeSort(a, c);
else
TimSort.sort(a, 0, a.length, c, null, 0, 0);
}
}
再进入legacyMergeSort(a, c)方法内部,有:
private static <T> void legacyMergeSort(T[] a, Comparator<? super T> c) {
T[] aux = a.clone();
if (c==null)
mergeSort(aux, a, 0, a.length, 0);
else
mergeSort(aux, a, 0, a.length, 0, c);
}
再点击进入mergeSrot(aux, a, 0, a.length, 0, c)方法内部,有:
private static void mergeSort(Object[] src,
Object[] dest,
int low, int high, int off,
Comparator c) {
int length = high - low;
// Insertion sort on smallest arrays
if (length < INSERTIONSORT_THRESHOLD) {
for (int i=low; i<high; i++)
for (int j=i; j>low && c.compare(dest[j-1], dest[j])>0; j--)
swap(dest, j, j-1);
return;
}
// 省略无关代码
}
答案便在这了:
for (int j=i; j>low && c.compare(dest[j-1], dest[j])>0; j--)
swap(dest, j, j-1);
可以看出,当前者与后者的比较,compare方法返回1时,则将二者进行交换。回顾我们写的lambda表达式:
historyValues.sort(((o1, o2) -> {
if (o1.length() > o2.length()) {
return -1;
} else if (o1.length() < o2.length()) {
return 1;
}
return 0;
}));
当o1的长度<o2的长度,则返回1。也就是说,前者长度<后者长度时,则进行位置的交换,这也就实现了“长者在前、短者在后”的效果。
总结
不论比较的具体规则是什么,按照-1、0、1的顺序进行排序。