在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

 

在java中Comparable 和 Comparator 的接口及其应用

上一篇:概率论与随机过程之间的关系


下一篇:多个文件下载