7.排序比较要慎重

一灰灰blogJava编程技巧JDK编程技巧约 954 字大约 3 分钟

实战7:排序比较要慎重

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

1.排序

1.1. Comparator 与 Comparable

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

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

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

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来实现了

@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);
}

输出结果如下

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}]

1.2. 踩坑预告

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

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

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

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

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

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

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

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

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

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

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

  • 第一种实现方式,调换位置
  • Jdk1.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));

2. 小结

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

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

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

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

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