比较器的使用

一、TreeMap 实现排序

TreeMap是一个有序结构,TreeSet也是类似。

他们可以实现对元素的排序。TreeMap 是针对 key进行排序。

如果TreeMap的key 是Integer类型,可以无需指定任何特殊条件,默认即按照升序进行排序,如:

    public static void main(String[] args) {
        Object value = new Object();
        TreeMap<Integer, Object> intMap = new TreeMap<>();
        intMap.put(7, value);
        intMap.put(4, value);
        intMap.put(0, value);
        intMap.put(10, value);
        System.out.println(intMap);
    }
// 输出:
{0=java.lang.Object@4554617c, 4=java.lang.Object@4554617c, 7=java.lang.Object@4554617c, 10=java.lang.Object@4554617c}

那么如果想降序,或者key不是一个 Integer类型,该如何排序?

这时我们需要指定一个比较器:

    public static void main(String[] args) {
        Object value = new Object();
        TreeMap<Integer, Object> intMap = new TreeMap<>((o1, o2) -> o2 - o1);
        intMap.put(7, value);
        intMap.put(4, value);
        intMap.put(0, value);
        intMap.put(10, value);
        System.out.println(intMap);
    }
// 输出:
{10=java.lang.Object@f6f4d33, 7=java.lang.Object@f6f4d33, 4=java.lang.Object@f6f4d33, 0=java.lang.Object@f6f4d33}

这里有一个口诀可以快速记忆升序还是降序的比较器,我们知道比较器需要接收两个参数 o1 和 o2,那么如果是 o1 - o2 其排序结果就是升序,如果 o2 - o1 其排序结果就是降序:

排序器升降记忆口诀:

正减升,反减降

对于一个POJO 类型,如果有一个 Integer 类型的 age 属性,希望用 age 排序,就可以是 o1.getAge() - o2.getAge() ,这就是升序,降序就是颠倒过来。

二、不添加“重复”的 key

注意,TreeMap 通过key 的大小来判定顺序,它认为的 “重复” key 指的是大小比较相等的key,如果相等,便不会重复添加元素,这与HashMap 覆盖旧值是有区别的。

    public static void main(String[] args) {
        Student s1 = new Student(1, "s1", 24);
        Student s2 = new Student(2, "s2", 30);
        Student s3 = new Student(3, "s3", 21);
        Student s4 = new Student(4, "s3", 21);
        // TreeMap通过 key来进行排序,如果是可比较类型,直接排序
        // 如果不是可比较类型,需要令该类型实现Comparable接口,或构造器中指定排序器
        TreeMap<Student, String> stuMap = new TreeMap<>((o1, o2) -> o1.getAge() - o2.getAge());
        Comparator<? super Student> comparator = stuMap.comparator();
        stuMap.put(s2, s2.getName());
        stuMap.put(s3, s3.getName());
        stuMap.put(s1, s1.getName());
        stuMap.put(s4, s4.getName());

        System.out.println(stuMap);
    }

输出结果:

{Student{id=3, name='s3', age=21}=s3, Student{id=1, name='s1', age=24}=s1, Student{id=2, name='s2', age=30}=s2}

可以看到由于 age = 21 的Student有s3 和 s4,但却没有添加 s4。

如果“重复”了,却又想保留“重复”元素,原因 它们虽然排序属性相等,但并不是完全相同的两个对象,该如何做?

可以使用hashCode(但是存在冲突的风险),或直接使用内存地址来进一步比较。

三、比较器优先

对于POJO 实现了 Comparable 的情况,我们需要实现它的 compareTo 方法:

class Student implements Comparable<Student> {
    private Integer id;
    private String name;
    private Integer age;

    @Override
    public int compareTo(Student o) {
        return this.age - o.getAge();
    }
    // 构造器和 getter、setter
}

那么 o1 就是当前 this 对象,o2 就是参数对象,同样遵循 “正减升,反减降”的口诀。

不过如果同时指定了排序器和 Comparable ,而这两种逻辑又存在冲突,该如何选择呢?

答案是比较器优先,原因是比较器的位置更接近客户代码,在排序前指定排序规则,要比类定义时指定排序规则更加明确,因此 TreeMap 一类有序结构都是按照排序器优先的原则来排序的。

 

 

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

打赏
文章很值,打赏犒劳作者一下
相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页

打赏

圣斗士Morty

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者