排序是编程中经常需要用到的功能,而 Java 的工具类 Collections 中也提供了 sort 方法用于实现对列表等集合中元素的排序。Collections.sort() 方法有两种形式:Collections.sort(List)
和 Collections.sort(List, Comparator)
。
第一种 Collections.sort(List)
要求 List 中的元素已经实现了 Comparable 接口,第二种 Collections.sort(List, Comparator)
则需要实现一个比较器。基于比较的排序的关键就是能比较两个元素的大小,知道了谁大谁小,才能确定谁排在前面谁排在后面;而这两种方法的本质都是为了确定两个元素谁大谁小。接下来主要谈一谈基于比较器的自定义排序。
先看个排序示例:
public static void main(String[] args) {
Integer[] input = { 1, 3, 8, 7, 4, 9, 2 };
List lst = Arrays.asList(input);
Collections.sort(lst);
System.out.println(lst);
}
//输出为: [1, 2, 3, 4, 7, 8, 9]
可以看到,原本无序的列表变成了从小到大排列。原因有二:
- Integer 类实现了 Comparable 接口,使得元素之间可以比较大小
- sort 方法默认是从小到大排列(这点非常重要)
然而大多数情况下,我们想要的都不是简单的升序排列,这时候自定义排序就要派上用场了。Comparator 实现自定义排序的关键是实现 compare 方法,该方法声明为:
int compare(T o1, T o2);
当方法返回负值时,代表 o1 < o2;当方法返回 0 时,代表 o1 = o2;当方法返回正值时,代表 o1 > o2;下面以实现降序排列为例,演示如何使用 Comparator。
###一、降序排列
public class Main {
public static void main(String[] args) {
Integer[] input = { 1, 3, 8, 7, 4, 9, 2 };
List lst = Arrays.asList(input);
Collections.sort(lst, new IntComparator());
System.out.println(lst);
}
}
class IntComparator implements Comparator {
public int compare(T a, T b) {
return (Integer)b - (Integer)a;
}
}
//输出为: [9, 8, 7, 4, 3, 2, 1]
为什么这样就实现了降序排列呢?
这是因为当传入 Comparator 参数时, sort 方法会将需要比较大小的两个元素 a,b 作为参数传给 Comparator.compare(T a, T b) 方法,根据返回的结果判断 a,b 的大小。例如,传入1 和 3 的时候,compare 返回 2,正值代表 a > b,即 1 “大于” 3,这个“大于”正是我们自己定义的规则。然后,根据 sort 方法默认从小到大排序的性质,小的排在前面,所以最后 3 排在 1 前面,也就实现了降序排列。
在这里 return (Integer)b - (Integer)a;
是一种经验写法,如果分解开来应该是这样:
class IntComparator implements Comparator {
public int compare(T a, T b) {
int num1 = (Integer)a;
int num2 = (Integer)b;
if(num1 > num2) { //如果 a>b,根据自定义规则 a 应该在前面,即自定义规则中 a “小于” b,故此时返回负值
return -1;
} else if(num1 < num2) {
return 1;
} else {
return 0;
}
}
}
该 compare 方法非常简单,但在实际应用中,由于自定义规则的复杂性,compare 的编写难度也可能变得很大。下面再举一例,让大家加深对 compare 的理解。
###二、奇偶排列 要求:奇数排在偶数前面,奇数之间从小到大排列,偶数之间从大到小排序
class IntComparator implements Comparator {
public int compare(T a, T b) {
int num1 = (Integer)a;
int num2 = (Integer)b;
if(num1 % 2 == 1 && num2 % 2 == 1) {
return num1 - num2; // ①
} else if(num1 % 2 == 0 && num2 % 2 == 0) {
return num2 - num1; // ②
} else if(num1 % 2 == 1 && num2 % 2 == 0) {
return -1; // ③
} else {
return 1; // ④
}
}
}
① ② 表示两数同奇或同偶的情况,此时根据经验写法很容易得到。
③ ④ 表示奇偶不同的情况,此时若 a 为奇数,b 为偶数,则 a 应该排在 b 前面,即 a “小于” b,故返回负值;反之,返回正值。
最后,再次强调:排在前面的元素“小”,排在后面的元素“大”。
留个练习,有兴趣的朋友可以做下,看看自己是不是真的掌握了。
练习题:实现数字按照高位到低位的比较,进行降序排序;高位相同的,位数少的排前面。例如:输入 {3, 34, 5, 7, 56, 523, 52, 571};输出 {7, 5, 571, 56, 52, 523, 3, 34}