ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

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

2022-02-08 16:59:11  阅读:200  来源: 互联网

标签:Comparable score Comparator list 接口 源码 Student 排序 public


文章目录

前言

对集合中的元素排序,我们可以使用 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,score,Comparator,list,接口,源码,Student,排序,public
来源: https://blog.csdn.net/a1241692733/article/details/122815693

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有