这里所说到的Java中的排序并不是指插入排序、希尔排序、归并排序等具体的排序算法。而是指执行这些排序算法时,比较两个对象“大小”的比较操作。
为了解决如何比较两个对象大小的问题,JDK提供了两个接口 java.lang.Comparable 和 java.util.Comparator 。
一、自然排序:java.lang.Comparable
Comparable接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的 compareTo(T t) 方法被称为它的自然比较方法。
/*Comparable接口中只提供了一个方法:compareTo(Object obj), 该方法的返回值是int。
如果返回值为正数,则表示当前对象(调用该方法的对象)比obj对象“大”;反之“小”;为零
则表示两对象相等。示例如下:*/
public class Stu implements Comparable {
private int id;
private String name;
public Stu() {
super();
}
@Override
public int compareTo(Obj obj) {
if (obj instanceof Stu) {
Stu stu = (Stu) obj;
return id - stu.id;
}
return 0;
}
}
实现Comparable接口的对象列表(和数组)可以通过 Collections.sort(和 Arrays.sort)进行自动排序。实现此接口的对象可以用作有序映射中的键或有序集合中的元素,无需指定比较器。
二、定制排序:java.util.Comparator
强行对某个对象 collection 进行整体排序 的比较函数。可以将 Comparator 传递给 sort 方法(如 Collections.sort 或 Arrays.sort),从而允许在排序顺序上实现精确控制。还可以使用 Comparator 来控制某些数据结构(如有序 set或有序映射)的顺序,或者为那些没有自然顺序的对象 collection 提供排序。
/**
*以两段优先队列的代码进行示例:
*优先队列升序降序:
*/
PriorityQueue<Integer> queueUp = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
//升序 o1 - o2 为正数时候,表示需要调整次序
return o1 - o2;
}
});
PriorityQueue<Integer> queueDown = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
//降序
return o2 - o1;
}
});
/**
*以两段优先队列的代码进行示例:
*优先队列的典例!
*leetCode1405. 最长快乐字符串
*代码来自三叶大佬。
*/
public String longestDiverseString(int a, int b, int c) {
//lambda表达式简写。
//如果y[1] - x[1] > 0, 则表示需要调整次序,即 x,y 顺序变为 y,x 顺序
//如果y[1] - x[1] < 0, 则表示无需调整次序,即 x,y 顺序保留;
//所以队列将,按照PriorityQueue<int[]> q 里的每个 int[] each 的 each[1]
//进行降序排列。
PriorityQueue<int[]> q = new PriorityQueue<>((x, y) ->y[1] - x[1]);
if (a > 0) q.add(new int[]{0, a});
if (b > 0) q.add(new int[]{1, b});
if (c > 0) q.add(new int[]{2, c});
StringBuilder sb = new StringBuilder();
while (!q.isEmpty()) {
//每次取出堆顶元素(剩余数量最多的字符)
int[] cur = q.poll();
int n = sb.length();
if (n >= 2 && sb.charAt(n - 1) - 'a' == cur[0] && sb.charAt(n - 2) - 'a' == cur[0]) {
if (q.isEmpty()) break;
//取出数量次最多的字符
int next[] = q.poll();
sb.append((char)(next[0] + 'a'));
if (--next[1] != 0) q.add(next);
q.add(cur);
} else {
sb.append((char) (cur[0] + 'a'));
if (--cur[1] != 0) q.add(cur);
}
}
return sb.toString();
}
//优先队列只是保证了每次出队的是最大或者最小的,并不是在队列内部完全排序完成再出队。
三、自然排序和定制排序对比
//自然排序和定制排序的核心代码:
//Ⅰ
public int compareTo(Obj obj) {
if (obj instanceof Stu) {
Stu stu = (Stu) obj;
return id - stu.id;
}
return 0;
}
//Ⅱ
public int compare(Integer o1, Integer o2) {
//升序 o1 - o2 为正数时候,表示需要调整次序
return o1 - o2;
}
- 自然排序于某个类的内部写定,是的这一整个类型的所有对象都可以(默认)按照这个规则来比较大小;而定制排序则是在某次比较中声明的函数,其优先级高于自然排序。
- 两者的核心代码如上,都遵循这样的规则:返回值为正数,则需要调整次序;返回这为负数,则无需调整次序;返回值为0表示两者相等。上述代码中,Ⅱ部分,默认顺序为o1, o2; Ⅰ部分, 默认顺序为this(调用者), obj。
- 当元素的类型没有实现java.lang.Comparable接口而又不方便修改代码,或者实现了java.lang.Comparable接口的排序规则不适合当前的操作,那么可以考虑使用 Comparator 的对象来排序。