zoukankan      html  css  js  c++  java
  • 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算法题
    分布式
    linux中文件描述符fd和struct file结构体的释放
    Linux字符设备驱动
    Linux内存地址管理概述
    mnist卷积网络实现
    【TensorFlow官方文档】MNIST机器学习入门
    FCN笔记
    datetime.timedelta
    tensorflow中的函数获取Tensor维度的两种方法:
  • 原文地址:https://www.cnblogs.com/dogeLife/p/11581204.html
Copyright © 2011-2022 走看看