210817-实战小技巧7:排序比较要慎重

文章目录
  1. 1. Comparator 与 Comparable
  2. 2. 踩坑预告
  3. 3. 小结
  4. 4. 实战系列推荐
  • II. 其他
    1. 1. 一灰灰Blog: https://liuyueyi.github.io/hexblog
    2. 2. 声明
    3. 3. 扫描关注
  • 每天一个实战小技巧:排序比较要慎重

    今天介绍的又是一个非常非常基本的基本知识点,为啥要单独拎出来?还是因为这个东西虽然非常简单,但是很容易掉坑,我已经遇到几次不严谨的写法了

    1. Comparator 与 Comparable

    输掉排序,这两个接口好像不太容易绕过去,我们简单介绍下它们的区别

    • 如果你有一个类,希望支持同类型的自定义比较策略,可以实现接口Compareable
    • 如果某个类,没有实现Compareable接口,但是又希望对它进行比较,则可以自自定义一个Comparator,来定义这个类的比较规则

    通过一个简单的实例进行演示说明

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    public static class Demo implements Comparable<Demo> {
    int code;
    int age;

    public Demo(int code, int age) {
    this.code = code;
    this.age = age;
    }

    @Override
    public int compareTo(Demo o) {
    if (code == o.code) {
    return 0;
    } else if (code < o.code) {
    return -1;
    } else {
    return 1;
    }
    }

    @Override
    public String toString() {
    return "Demo{" +
    "code=" + code +
    ", age=" + age +
    '}';
    }
    }

    上面的实现中,重点关注 Demo类,实现了Comparable接口,因此可以直接调用list.sort(null)来进行比较;

    但是如果我们现在需求改变了,希望实现针对demo类的age字段,进行升序排列,那么就可以利用Comparator来实现了

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    @Test
    public void testDemoSort() {
    List<Demo> list = new ArrayList<>();
    list.add(new Demo(10, 30));
    list.add(new Demo(12, 10));
    list.add(new Demo(11, 20));
    // 默认根据 code 进行升序比较
    list.sort(null);
    System.out.println("sort by code: " + list);

    list.sort(new Comparator<Demo>() {
    @Override
    public int compare(Demo o1, Demo o2) {
    if (o1.age == o2.age) {
    return 0;
    } else if (o1.age < o2.age) {
    return -1;
    } else {
    return 1;
    }
    }
    });
    System.out.println("sort by age: " + list);
    }

    输出结果如下

    1
    2
    sort by code: [Demo{code=10, age=30}, Demo{code=11, age=20}, Demo{code=12, age=10}]
    sort by age: [Demo{code=12, age=10}, Demo{code=11, age=20}, Demo{code=10, age=30}]

    2. 踩坑预告

    再上面的compare方法实现中,我们可以发现里面的实现有点不太美观,我们最终的目的是什么?

    • 如果左边的小于右边的,返回 -1
    • 如果左边的大于右边的,返回 0
    • 如果左边的等于右边的,返回 1

    基于此,经常可以看到的实现如下

    1
    2
    3
    4
    5
    6
    list.sort(new Comparator<Demo>() {
    @Override
    public int compare(Demo o1, Demo o2) {
    return o1.age - o2.age;
    }
    });

    上面这个实现虽然简洁了,但是有一个致命的问题,可能溢出!!!

    所以请注意,千万千万不要用上面这种写法

    那么有没有更优雅的方式呢?

    • 有,使用基础类的compare方法
    1
    2
    3
    4
    5
    6
    list.sort(new Comparator<Demo>() {
    @Override
    public int compare(Demo o1, Demo o2) {
    return Integer.compare(o1.age, o2.age);
    }
    });

    上面这一段代码,再jdk1.8中,可以简化为下面一句

    1
    list.sort(Comparator.comparingInt(o -> o.age));

    再扩展一下,如果希望倒排呢?

    • 第一种实现方式,调换位置
    • Jdk1.8方式,使用负数
    1
    2
    3
    4
    5
    6
    7
    8
    list.sort(new Comparator<Demo>() {
    @Override
    public int compare(Demo o1, Demo o2) {
    return Integer.compare(o2.age, o1.age);
    }
    });

    list.sort(Comparator.comparingInt(o -> -o.age));

    3. 小结

    今天主要介绍的知识点是排序,再我们日常使用中,如果一个类希望支持排序,最好的方式就是让它实现Comparable接口,然后自定义排序方式

    这样再容器中,如果需要排序,直接调用 list.sort(null) 或者 CollectionUtils.sort(list)

    如果目标类没有实现排序接口,或者希望使用另外一种排序方式,则通过自定义的Comparator来实现

    最后关于compare方法的实现,设计到两个类的比较,这种最终的落脚地,多半是基础类型的比较

    • o1 与 o2 比较,返回负数,则最终的结果中o1再前面(即升序排列)
    • 不要直接使用 o1-o2会溢出,推荐使用 Integer.compare(o1, o2);

    4. 实战系列推荐

    II. 其他

    1. 一灰灰Bloghttps://liuyueyi.github.io/hexblog

    一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

    2. 声明

    尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

    • 微博地址: 小灰灰Blog
    • QQ: 一灰灰/3302797840
    • 微信公众号: 一灰灰blog

    3. 扫描关注

    一灰灰blog

    QrCode

    评论

    Your browser is out-of-date!

    Update your browser to view this website correctly. Update my browser now

    ×