在java中Comparable 和 Comparator 的接口及其应用
Comparable 接口
我们常常看到这样一句话
Arrays 类中的 sort 方法承诺可以对对象数组进行排序,但要求满足下列条件:对象所属的类必须实现 Comparable 接口,重写 compareTo 方法
Comparable 代码如下:
1 public interface Comparable<T> {
2 int compareTo(Object other);
3 }
比如在自己定义的 Employee 类中,对两个 Employee 实例, 想要根据各自的工资属性进行比较,则可以让 Employee 实现 Comparable 接口,并重写 compareTo 方法:
1 public int compareTo(Object otherObject){
2 Employee other = (Employee) otherObject;
3 return Double.compare(salary, other.salary);
4 // return this.salary - other.salary;
5 }
这里,使用了静态 Double.compare 方法,如果第一个参数小于第二个参数,会返回负值;如果二者相等,会返回0;否则,返回一个正值。也可以使用注释掉的方式。
通过在 Employee 类中实现 Comparable 接口,并重写 compareTo 方法,Employee 类的实例变量就可以通过 salary 属性进行比较了。比如有一个 Employee 的数组,可以通过
1 Employee[] employees = new Employee[5];// 并添加实例 2 Arrays.sort(employees);
对 employees 数组进行升序排序,如果想要按照 salary 降序排序,只需要将 compareTo 改为
1 public int compareTo(Object otherObject){
2 2 Employee other = (Employee) otherObject;
3 3 return Double.compare(other.salary, salary);
4 4 // return other.salary - this.salary;
5 5 }
那么如果是一个 String 类型的数组呢?如果直接调用
Arrays.sort()
则是默认按照字典序进行排序的,因为 String 类自己实现过 Compareble<String> 接口,按照字典序进行比较。现在假设我们需要按照字符串长度对字符串进行比较,总不能去更改 String 的源码吧!况且 String 也不让我们改。为了处理这种情况,Arrays.sort() 还有第二个版本,让我们自己传入一个比较器进行比较,这个比较器就是实现了 Comparator 接口的实例。
Comparator 接口
1 public interface Comparator<T> {
2 int compare(T first, T second);
3 }
如果要按照字符串长度进行比较,可以先声明一个一个实现了 Comparator 接口的比较类:
1 class LenComparator implements Comparator<String> {
2 public int compare(String first, String second) {
3 return first.length() - second.length();
4 }
5 }
再利用 Arrays.sort 进行排序
Arrays.sort(strs, new LenComparator());
也可以写成内部类的形式,比如下面对于字符串可变数组的排序:
1 public static void sortStrings() {
2 String[] strs = new String[4];
3 strs[0] = "dwefrwf";
4 strs[1] = "12w";
5 strs[2] = "w212";
6 strs[3] = "1we2rwqw2re1e2";
7
8 List<String> strings = new ArrayList<>(Arrays.asList(strs));
9
10 Collections.sort(strings, new Comparator<String>() {
11 @Override
12 public int compare(String o1, String o2) {
13 return o1.length() - o2.length();
14 }
15 });
16
17 for (String s : strings) {
18 System.out.print(s + " ");
19 }
20 }
输出为
1 12w w212 dwefrwf 1we2rwqw2re1e2
总结
总结一点,如果对于我们自己创建的 class 类,需要对其实例进行按照某种规则比较,则应该让其类实现 Comparable 接口,并重写 compareTo 方法;如果是调用的别人的类,且不能轻易更改这个类的源码,或者想要按照新的比较方式对两个实例进行比较,就需要根据 Comparator 接口自己定义比较器了。
Map 按照 key 进行排序
之前在写程序的过程中,经常会遇到想要对映射中的键值对按照 键 或者 值 进行比较排序,如果想要按照 key 进行升序排序,Java 为我们提供了 TreeMap,它是按照 key 升序排列的有序映射。但是如果我们想要按照 key 降序排列呢,就得在声明 TreeMap 的时候为其指定比较器。比如下面一个方法,对给定数组,要统计数组中每个元素出现的次数,并且按照元素值降序排列。可以先声明一个 TreeMap 映射,为其注入按照 key 进行比较的比较器实例,这里使用的也是内部类的方式将实例注入进去,与上面的声明实例的方式有些许区别。
1 public static void orderByKey(int[] nums) {
2 Map<Integer, Integer> map = new TreeMap<>(
3 new Comparator<Integer>() {
4 public int compare(Integer obj1, Integer obj2) {
5 // 按照 key 降序排序
6 //return obj2.compareTo(obj1);
7 return obj2 - obj1;
8 // 按照 key 升序排序,TreeMap 默认的排列方式
9 // return obj1.compareTo(obj2);
10 }
11 }
12 );
13
14 for (int i=0; i<nums.length; i++) {
15 map.put(nums[i], map.getOrDefault(nums[i], 0) + 1);
16 }
17 for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
18 System.out.println(entry.getKey() + " : " + entry.getValue());
19 }
20 }
然后测试一把
1 int[] nums = {4,1,-1,2,-1,2,3};
2 orderByKey(nums);
输出
1 4 : 1 2 3 : 1 3 2 : 2 4 1 : 1 5 -1 : 2
可以看到所得到的映射是按照 key 降序排序输出的。
Map 按照 value 进行排序
如果要将映射里的内容按照 value 进行排序呢?该如何操作,这里不能直接在声明 Map 的时候为其注入比较器实例,因为按照 value 进行比较,map 都还没初始化,是不能获取到 key 对应的 value 值的,所以需要借助 Collectins,.sort() 方法对键值对进行 sort 排序,再将排好序的键值对放入新的 Map 里。
1 public static void orderByValue(int[] nums) {
2 Map<Integer, Integer> beforeSort = new TreeMap<>();
3 for (int i=0; i<nums.length; i++) {
4 beforeSort.put(nums[i], beforeSort.getOrDefault(nums[i], 0) + 1);
5 }
6
7 List<Map.Entry<Integer,Integer>> list = new ArrayList<>(beforeSort.entrySet());
8 Collections.sort(list, new Comparator<Map.Entry<Integer, Integer>>() {
9 public int compare(Map.Entry<Integer, Integer> o1,
10 Map.Entry<Integer, Integer> o2) {
11 //降序
12 // return o2.getValue().compareTo(o1.getValue());
13
14 // 升序
15 return o1.getValue().compareTo(o2.getValue());
16
17 }
18 });
19
20 // 将排好序的 entry 放入有序映射中
21 Map<Integer, Integer> afterSort = new LinkedHashMap<>();
22 for (Map.Entry<Integer, Integer> entry : list) {
23 afterSort.put(entry.getKey(), entry.getValue());
24 }
25
26 for (Map.Entry<Integer, Integer> entry : list) {
27 System.out.println(entry.getKey() + " : " + entry.getValue());
28 }
29 }
测试一把
1 int[] nums = {4,1,-1,2,-1,2,3};
2 orderByValue(nums);
输出为:
1 1 : 1 2 3 : 1 3 4 : 1 4 -1 : 2 5 2 : 2
浙公网安备 33010602011771号