一直想不明白,compare函数如何通过返回值,就确定是升序还是降序呢,于是乎决定好好的先看看源码。
下面的代码实现了对自己写的Students类按照年龄升序的排序,就是通过对实现comparator接口,重写compare的方法,实现对排序的升序或者降序的控制:
import java.util.Arrays;
import java.util.Comparator;
public class learn_comparator{
public static class Student {
public int id;
public String name;
public int age;
public Student(String name, int age, int id) {
this.id = id;
this.name = name;
this.age = age;
}
}
//重写compare方法,实现按照年龄的升序排列
public static class ageAscendingComparator implements Comparator<Student>{
@Override
public int compare(Student o1, Student o2) {
return o1.age-o2.age;
}
public static void printStudent(Student[] stu){
for (int i = 0; i " ");
System.out.print(stu[i].name);
System.out.print(" ");
System.out.println(stu[i].age);
}
}
public static void main(String[] args) {
Student s1=new Student("A",11,1);
Student s2=new Student("B",18,2);
Student s3=new Student("C",38,3);
Student s4=new Student("D",20,4);
Student s5=new Student("E",30,5);
Student s6=new Student("F",17,6);
Student[] arr_stu=new Student[]{s1,s2,s3,s4,s5,s6};
System.out.println("未排序前的输出如下:");
printStudent(arr_stu);
System.out.println("按照年龄升序排列后输出如下:");
Arrays.sort(arr_stu,new ageAscendingComparator());
printStudent(arr_stu);
}
}
输出结果:
未排序前的输出如下:
1 A 11
2 B 18
3 C 38
4 D 20
5 E 30
6 F 17
按照年龄升序排列后输出如下:
1 A 11
6 F 17
2 B 18
4 D 20
5 E 30
3 C 38
Comparator是一个接口,它包含两种方法
int compare(T o1, T o2); boolean equals(Object obj);
一个类要实现Comparator接口,可以只实现compare函数,因为任何类,默认都是已经实现了equals(Object obj)的。 Java中的一切类都是继承于java.lang.Object,在Object.java中实现了equals(Object obj)函数
public boolean equals(Object obj) { return (this == obj); }
所以,其它所有的类也相当于都实现了该函数
①Arrays.sort(arr_stu,new ageAscendingComparator());
当程序走到这的时候调用了public static
这个函数(IDEA中ctrl+鼠标左键可以查看函数),代码如下
至于对泛型 extends T>和 super T>的用法
参考:这篇文章
public static void sort(T[] a, Comparator super T> c) {
if (c == null) {
sort(a);
} else {
if (LegacyMergeSort.userRequested)
/*没看明白userRequested怎么得到的,没有走到这里 */
legacyMergeSort(a, c);
else//函数走到了这
TimSort.sort(a, 0, a.length, c, null, 0, 0);
}
}
②TimSort.sort(a, 0, a.length, c, null, 0, 0);
static void sort(T[] a, int lo, int hi, Comparator super T> c,
T[] work, int workBase, int workLen) {
//assert不满足,就报错
assert c != null && a != null && lo >= 0 && lo <= hi && hi <= a.length;
int nRemaining = hi - lo;
if (nRemaining < 2)
return; // Arrays of size 0 and 1 are always sorted
//MIN_MERGE为常数32,如果数组长度小于32,则会调用countRunAndMakeAscending()
//函数(重点,升序、降序的关键在此函数里),升序或者降序的数最后一个数的下标+1,
//赋值给initRunLen 数组分为一部分有序、一部分无序。
//然后使用二分查找插入排序,完后所有的排序
if (nRemaining < MIN_MERGE) {
int initRunLen = countRunAndMakeAscending(a, lo, hi, c);
binarySort(a, lo, hi, lo + initRunLen, c);
return;
}
***************************************************
******** >=32的部分,还未看 **************
***************************************************
}
③countRunAndMakeAscending(a, lo, hi, c);
这是最重要的的部分了
private static int countRunAndMakeAscending(T[] a, int lo, int hi,
Comparator super T> c) {
assert lo < hi;
//移动runHi来遍历
int runHi = lo + 1;
if (runHi == hi)
return 1;
// Find end of run, and reverse range if descending
if (c.compare(a[runHi++], a[lo]) < 0) { // Descending
//本例中,后面的只要比前面的小,就一直往后数
while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) < 0)
runHi++;
//把降序反转
reverseRange(a, lo, runHi);
} else { // Ascending
//本例中,后面的只要比前面的大,就一直往后面数
while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) >= 0)
runHi++;
}
//返回升序的边界
return runHi - lo;
}
到此为止,程序已经把数组分成了前一部分升序和后一部分无序了。接下来该用二分查找排序完成排序了
对于二分查找排序的理解,可以参考这篇文章
④ binarySort(a, lo, hi, lo + initRunLen, c);
private static void binarySort(T[] a, int lo, int hi, int start,
Comparator super T> c) {
//start=lo + initRunLen就是找到的升序的边界
assert lo <= start && start <= hi;
if (start == lo)
start++;
//遍历无序部分的每一个元素
for ( ; start < hi; start++) {
T pivot = a[start];
int left = lo;
int right = start;
assert left <= right;
while (left < right) {
//右移一位相当于除以二
int mid = (left + right) >>> 1;
if (c.compare(pivot, a[mid]) < 0)
//将范围向左,缩小一半
right = mid;
else
//向右缩小一半
left = mid + 1;
}
assert left == right;
int n = start - left; // 需要移动的位数
switch (n) {
case 2: a[left + 2] = a[left + 1];
case 1: a[left + 1] = a[left];
break;
//要是移动的位数超过2,就用系统提供的方法;
default: System.arraycopy(a, left, a, left + 1, n);
}
a[left] = pivot;
}
}