源码详解 Comparable 和 Comparator 接口, compareTo 方法和 compare 方法的区别和使用

2022/2/8 17:42:54

本文主要是介绍源码详解 Comparable 和 Comparator 接口, compareTo 方法和 compare 方法的区别和使用,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

文章目录

  • 前言
  • 1.这里为什么是升序排序?
    • 1.1 我们来看看 JDK1.8 的源码
    • 1.2 Comparable 接口
    • 1.3 Comparable 接口中的 compareTo 方法和排序有什么关系?
  • 2.怎么改为降序呢?
    • 2.1 Comparator 接口
    • 2.2 Comparator 接口中的 compare 方法和排序有什么关系?
    • 2.3 实现 Comparator 接口,改为降序排序
    • 2.4 使用匿名内部类和 lambda 表达式
  • 3.对一个学生的属性进行排序
    • 3.1 Student 类实现 Comparable 接口
    • 3.2 基于 Comparator 接口,自定义比较器
  • 4.总结
    • 4.1 Comparable 接口 和 Comparator 接口比较

前言

对集合中的元素排序,我们可以使用 Collections 工具类如:

	List<Integer> list1 = new ArrayList<>();
	list1.add(2);
	list1.add(1);
	list1.add(5);
	System.out.println(list1);
	Collections.sort(list1);
	System.out.println(list1);
	// 输出
	[2, 1, 5]
	[1, 2, 5]

为什么是升序排序?
怎么改为降序呢?
如果想要对一个学生的属性进行排序?如:

class Student {
    public String name;
    public int age;
    public double score;

    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }
}
public class TestDemo {
    public static void main(String[] args) {
        List<Student> list = new ArrayList<>();
        list.add(new Student("张三", 12, 70));
        list.add(new Student("李四", 18, 81));
        list.add(new Student("王五", 21, 93));
        
        System.out.println(list);
        // 显然这里是不行的
        Collections.sort(list);
        System.out.println(list);
    }
}

显然这里是不行的,谁知道这是按学生的哪个属性排序,那么怎么去自定义排序的方式呢?


1.这里为什么是升序排序?

先说结论:Integer 包装类 实现了Comparable 接口,重写了 compareTo 方法,而 compareTo 方法的返回值影响排序是升序还是降序

1.1 我们来看看 JDK1.8 的源码

Collections.sort 源码:
在这里插入图片描述
我们这里的 List 保存的是 Integer 类型,源码这里可知 Integer 一定实现了Comparable 接口
我们看看 Integer包装类 确实继承了Comparable 接口
在这里插入图片描述

1.2 Comparable 接口

再看看 Comparable 接口,接口中只有一个 compareTo 的抽象方法, 返回一个 int 类型的值在这里插入图片描述
Integer 包装类中重写 compareTo 方法,compareTo 方法中调用了 compare 方法在这里插入图片描述
compare 方法返回的 0 或者 -1 或者 1 取决于传入两数的大小,在 x < y返回 -1,x > y 返回1,x = y 返回 0;
在这里插入图片描述

1.3 Comparable 接口中的 compareTo 方法和排序有什么关系?

sort 传入null
在这里插入图片描述
这里圈起来的都为 null
在这里插入图片描述
在这里插入图片描述
点进这里
在这里插入图片描述
继续
在这里插入图片描述
终于破案了,我们的 list 一路传递到这里,在这里调用了 刚才的 compareTo 方法,这里就是排序的部分代码,compareTo 方法的返回值影响排序是升序还是降序如果前一个数大于后一个数,返回1,交换顺序,升序排序。
在这里插入图片描述

2.怎么改为降序呢?

由上边的分析我们可知只需修改 compareTo 的比较方式就行,但是这里的 compareTo 方法是 Integer 包装类中重写的,我们没办法修改。那让我们再看看另外一个 sort 的重载方法,发现还有第二个参数
在这里插入图片描述

2.1 Comparator 接口

Comparator 接口中有一个 compare的抽象方法,也是返回一个 int 类型
在这里插入图片描述

2.2 Comparator 接口中的 compare 方法和排序有什么关系?

这次传入的第二个参数不为 null,进入这里
在这里插入图片描述
在这里插入图片描述
最后同 compareTo 一样,在最后 compare 比较返回值影响排序的结果
在这里插入图片描述

2.3 实现 Comparator 接口,改为降序排序

不会写没关系,直接复制刚才 Integer 里的代码,修改 (x < y) ? -1 : ((x == y) ? 0 : 1); 修改为
(x > y) ? -1 : ((x == y) ? 0 : 1),相当于调换了 1 和 -1 的返回

class MyCompare implements Comparator<Integer> {
    @Override
    public int compare(Integer x, Integer y) {
        return (x > y) ? -1 : ((x == y) ? 0 : 1);
    }
}
public class TestDemo {
    public static void main(String[] args) {
        List<Integer> list1 = new ArrayList<>();
        list1.add(2);
        list1.add(1);
        list1.add(5);
        System.out.println(list1);
        MyCompare myCompare = new MyCompare();
        Collections.sort(list1, myCompare);
        System.out.println(list1);
	}
}
// 输出:
[2, 1, 5]
[5, 2, 1]

我们还可以将 compare 方法修改为这样,这是相同的意思,而且更简便。

class MyCompare implements Comparator<Integer> {
    @Override
    public int compare(Integer x, Integer y) {
        return y - x;
    }
}

2.4 使用匿名内部类和 lambda 表达式

以下几种方式都可以实现降序排序,为方便记忆,o1 作为第一个参数, o2 作为第二个参数,o2 - o1 降序(2 ,1),o1 - o2 升序(1,2)

	Collections.sort(list1, new Comparator<Integer>() {
	    @Override
	    public int compare(Integer o1, Integer o2) {
	        return o2 - o1;
	    }
	});
	// lambda 表达式
	Collections.sort(list1, (Integer o1, Integer o2) -> {return o2 - o1; });
	// 简写
	Collections.sort(list1, (o2, o1) -> o2 - o1);
	// Collections.sort 也是调用了list.sort,所以也可以直接这样写
	list1.sort((o2, o1) -> o2 - o1);

3.对一个学生的属性进行排序

3.1 Student 类实现 Comparable 接口

因为 Student 是我们写的类,那么就可以学这 Integer 包装类 去实现 Comparable 接口,先看看刚才 Integer 是怎么写的
在这里插入图片描述
this.value 作为第一个参数,compareTo 传入的参数作为第二个参数
在这里插入图片描述
我们也可以理解为

    public int compareTo(Integer o) {
        return this.value- o.value;
    }

自己实现

class Student implements Comparable<Student>{
    public String name;
    public int age;
    public double score;

    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }

    @Override
    public int compareTo(Student o) {
        return this.age - o.age;
    }
}
public class TestDemo {
    public static void main(String[] args) {
        List<Student> list = new ArrayList<>();
        list.add(new Student("张三", 12, 70));
        list.add(new Student("李四", 21, 81));
        list.add(new Student("王五", 18, 93));
        System.out.println(list);
        Collections.sort(list);
        System.out.println(list);
    }
}
// 运行结果,按年龄升序排序
[Student{name='张三', age=12, score=70.0}, Student{name='李四', age=21, score=81.0}, Student{name='王五', age=18, score=93.0}]
[Student{name='张三', age=12, score=70.0}, Student{name='王五', age=18, score=93.0}, Student{name='李四', age=21, score=81.0}]

这里我们看出来要修改比较的东西时,就可能要重新修改重写的方法,而正常情况下Student 是封装好的,所以这个局限性就比较大,这才有了 Comparator 接口,也称之为比较器
而刚才在创建类时 就实现 Comparable 接口 定义的排序方式称自然排序或内部排序

3.2 基于 Comparator 接口,自定义比较器

public class TestDemo {
    public static void main(String[] args) {
        List<Student> list = new ArrayList<>();
        list.add(new Student("张三", 12, 70));
        list.add(new Student("李四", 21, 81));
**加粗样式**        list.add(new Student("王五", 18, 93));
        System.out.println(list);
        // 自然排序
        Collections.sort(list);
        System.out.println(list);
        // 自定义比较器
        Collections.sort(list, (o1, o2) -> (int) (o2.score - o1.score));
        System.out.println(list);
    }
}

4.总结

Java 集合框架 Collections 工具类中的 sort 方法可以对集合中元素排序,前提是对象是可比较的,我们可以通过创建对象时实现Comparable 接口,或者在调用 sort 方法时将自己实现的 Comparator(比较器)接口作为参数传入。

4.1 Comparable 接口 和 Comparator 接口比较

  1. Comparable 接口在创建类时要手动实现并重写 compareTo 方法,一旦实现,每次用该类都有指定的顺序,属于内部顺序。如果要更换比较的方式,则要修改 compareTo 方法,侵入性强。

  2. Comparator(比较器)接口实现后作为参数传入Collections.sort。每次使用都要确定比较器,比较方式在实现比较器的时候重写 compare 方法确定,侵入性较弱。



这篇关于源码详解 Comparable 和 Comparator 接口, compareTo 方法和 compare 方法的区别和使用的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程