IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Java中的Comparable和Comparator到底该怎么用看完就全明白了 -> 正文阅读

[Java知识库]Java中的Comparable和Comparator到底该怎么用看完就全明白了

很多同学搞不清楚Comparable和Comparator这两个接口,单看这两个单词,一个是形容词一个是名词,但是可以看出来都和比较有关,我个人把实现了Comparable接口的某个类理解成这个类具备了比较能力,而把实现了Comparator的类称为比较器类,那么他们分别该怎么用呢?

Comparable(比较能力)

PriorityQueue队列

在正式介绍他们俩之前,先给大家介绍一下Java中的优先级队列PriorityQueue,这是一个实现了Queue接口的类,它的功能是add()进入一个个同类型对象,然后它会在内部进行操作,保证每次头删的时候都是队列中的最小的元素。既然要给出队列中的最小值,那么PriorityQueue在每次插入之后都会对其内部元素进行排序,才能保证队首是整个队列的最小值。下面演示一下:

import java.util.PriorityQueue;
import java.util.Queue;

public class Main {
    public static void main(String[] args) {
        Queue<Integer> queue = new PriorityQueue<>();

        //入队列
        queue.add(1);
        queue.add(999);
        queue.add(45);
        queue.add(3);
        queue.add(66);

        //看看队首元素
        System.out.println("对首元素" + queue.element());
        //出队列
        queue.remove();
        //看看对首元素、
        System.out.println("对首元素" + queue.element());
        //出队列
        queue.remove();
        //看看对首元素、
        System.out.println("对首元素" + queue.element());
        //出队列
        queue.remove();
        //看看对首元素、
        System.out.println("对首元素" + queue.element());
        //出队列
        queue.remove();
        //看看对首元素、
        System.out.println("对首元素" + queue.element());

    }
}

运行结果:
在这里插入图片描述
可以看到每次进行出队列操作后队首元素都是当前队列的最小值,而且这并不是我们插入时的顺序。所以现在可以确定的是每次出队列后PriorityQueue会选出队列中剩余元素中的最小值放在队首,可问题现在PriorityQueue是根据什么来找到的最小值呢?
大家可能会有疑问:1就是最小值啊,999就是最大值啊,这不很明显吗?
可是要知道的是java会对这些基本int类型作自动封装,队列中实际是Integer类的对象,所以问题就是PriorityQueue是怎么比较一个个Integer对象的?
我们点开Java中的Integer类看看:
在这里插入图片描述

从图中可以清楚的看到Integer类实现了Comparable接口,好了,说到这里我们来做个简单的总结,PriorityQueue可以将插入的所有元素中的最小值放在队首,怎么判断出哪个元素最小,对中元素实现了Comparable接口才能判断出哪个元素小那个元素大。
要是不实现Comparable接口怎么办呢?
我们来创建一个Student类放到PriorityQueue中看看。

//Student类
public class Student {
    public String name;
    public int grade;
    public int totalScore;

    public Student(String name, int totalScore){
        this.name = name;
        this.totalScore = totalScore;
    }

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

实例化几个student对象,然后试着把他们放到PriorityQueue中看看。

import java.util.PriorityQueue;
import java.util.Queue;

public class Main {
    public static void main(String[] args) {

        Queue<Student> queue = new PriorityQueue<>();

        Student a = new Student("张三", 555);
        Student b = new Student("李四", 378);
        Student c = new Student("王五", 456);
        Student d = new Student("魏六", 555);

        queue.add(a);
        queue.add(b);
        queue.add(c);
        queue.add(d);

        System.out.println(queue.remove());
    }
}

报错了:
在这里插入图片描述
可以看到在16行出的错:
在这里插入图片描述
从图中可以看到出错行16行是在执行b对象也就是第二个对象的插入时出了问题,为什么在插第一个对象的时候不报错呢?回想一下,PriorityQueue会把队列中的最小的元素放在队首,插入第一个对象时全队列只有一个元素所以不需要找最小对象,插第二个对象时队列中有两个元素了需要选两个中较小的一个的时候就出错了,因为我们Student类没有实现Comparable接口,所以PriorityQueue不能比较对象之间谁大谁小,所以在插入的时候报错了。那我们来实现Comparable接口:

public class Student implements Comparable<Student>{
    public String name;
    public int grade;
    public int totalScore;

    public Student(String name, int totalScore){
        this.name = name;
        this.totalScore = totalScore;
    }

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

    @Override
    public int compareTo(Student o) {
        return 0;
    }
}

再来执行上面的插入操作:

public class Main {
    public static void main(String[] args) {
        Queue<Student> queue = new PriorityQueue<>();

        Student a = new Student("张三", 555);
        Student b = new Student("李四", 378);
        Student c = new Student("王五", 456);
        Student d = new Student("魏六", 555);

        queue.add(a);
        queue.add(b);
        queue.add(c);
        queue.add(d);

        //头删
        queue.remove();
        
    }
}

可以看到因为我们的Student类实现了Comparable接口插入而插入成功了。并且在做出队列操作时也成功删除了。
我们再来捋一捋:因为我们的Student类实现了Comparable接口才能成功的入PriorityQueue队列,可是这里又有一个问题就是PriorityQueue是怎么知道我们插入的哪个元素是最小的?
在这里插入图片描述
这是我们Student类实现的Comparable中唯一的一个方法,PriorityQueue是根据这个方法的返回值比较元素大小的。它会两两对队列内的元素进行比较,谁大谁小是根据这个compareTo方法的返回值决定,比如PriorityQueue开始比较时先从例子中的a对象开始,那么它会再依次向a的compareTo()方法中传入b,c,d对象,这样比下来就知道了a和其他三个对象谁大谁小 ,我们要自己重写这个方法来规定是依据this的什么属性和传入对象的什么属性作比较的。
拿Student类来说它的属性有姓名,年级,总成绩三个,我们可以根据总成绩totalScore来做比较依据重写compareTo()方法。成绩高的的Student对象大,成绩低的对象小,这里比较绕,大家还是要明确我们Student类虽然实现了Comparable接口有了比较能力,能插入PriorityQueue,但是想让程序知道哪个对象大哪个对象小全是根据compareTo()方法的返回值看的。
Java默认的是compareTo()返回值为负则表示传入的o对象大,返回0表示一样大,正数表示this对象大。
我们重写Student类的compareTo()方法:

public class Student implements Comparable<Student>{
    public String name;
    public int grade;
    public int totalScore;

    public Student(String name, int totalScore){
        this.name = name;
        this.totalScore = totalScore;
    }

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

    @Override
    public int compareTo(Student o) {
        return this.totalScore - o.totalScore;
    }
}

当返回值为负数时表示this.totalScore < o.totalScore,也就是this对象比o对象小。
当返回值为0时表示this.totalScore = o.totalScore,也就是this对象和o对象一样大。
当返回值为正数数时表示this.totalScore > o.totalScore,也就是this对象比o对象大。

import java.util.PriorityQueue;
import java.util.Queue;

public class Main {
    public static void main(String[] args) {
        Queue<Student> queue = new PriorityQueue<>();

        Student a = new Student("张三", 555);
        Student b = new Student("李四", 378);
        Student c = new Student("王五", 456);
        Student d = new Student("魏六", 555);

        queue.add(a);
        queue.add(b);
        queue.add(c);
        queue.add(d);

        //看看头元素,因为我们是根据总成绩
        // totalScore来比较大小的,
        // 那么成绩小的会在队首
        System.out.println("对首元素" + queue.element());

    }
}

在这里插入图片描述
说到这里大家应该对Comparable的作用有所了解了,实现了它的类就有了比较能力,但是你想根据什么比较两个对象大小还得看你怎么重写compareTo()方法。如果还是迷的话请多卡看一看上面的叙述。

Comparator(比较器)

比较能力是一个类自己具备的,相应的比较器Comparator是要自己建造的,我们要为需要比较的类量身打造一个实现Comparator接口的比较器类,下面创建一个专为Student类量身打造的比较器:
我们先让Student类丧失比较能力也就是不实现Comparable接口,不重写compareTo()方法。

public class Student {
    public String name;
    public int grade;
    public int totalScore;

    public Student(String name, int totalScore){
        this.name = name;
        this.totalScore = totalScore;
    }

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

Student类的比较器类:

import java.util.Comparator;

public class Mycomparator implements Comparator<Student> {

    @Override
    public int compare(Student o1, Student o2) {
        //因为比较器和Student自己实现Comparable接口不一样
        // 比较器属于第三方,所以在使用时需要传入两个要比较的对象
        //两个对象的比较思路还是和Comparable一样的,拿总成绩比较
        //总成绩高的对象较大
        return o1.totalScore - o2.totalScore;
    }
}

他是这样用的:

import java.util.PriorityQueue;
import java.util.Queue;

public class Main {
    public static void main(String[] args) {
        Mycomparator myComparator = new Mycomparator();
        Queue<Student> queue = new PriorityQueue<>(myComparator);

        Student a = new Student("张三", 555);
        Student b = new Student("李四", 378);
        Student c = new Student("王五", 456);
        Student d = new Student("魏六", 555);

        queue.add(a);
        queue.add(b);
        queue.add(c);
        queue.add(d);

        //看看头元素,因为我们是根据总成绩
        // totalScore来比较大小的,
        // 那么成绩小的会在队首
        System.out.println("对首元素" + queue.element());

    }
}

运行结果:
在这里插入图片描述

在这里插入图片描述
在创建队列的时候就把比较器传进去。

小结

不管是Comparable(比较能力)和Comparator(比较器)都是用于我们实现的类作比较。Comparable是直接实现于我们创建的类,让这个类以后自己天然具备比较能力,Comparator是我们专为某一个类写的一个比较器类,当需要的时候我们得实例化一个比较器然后传入。至于具体是怎么比较那个对象大那个对象小我们就要根据自己的需要来重写方法了,在此提一下Java会根据这个方法的返回值判断两个对象谁大谁小。

在这里插入图片描述

对于实现Comparable接口的compareTo()方法来说返回值为正表示传入的对象大,this对象小;返回值为0两个对象一样大;返回值为负,表示传入的对象比this对象大。
在这里插入图片描述

对于实现Comparator接口的compare()方法来说返回值为正则传入的第一个对象大于第二个对象,返回值为0表示传入的两个对象一样大;返回值为负表示传入的第一个对象小于第二个对象。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-08-27 11:44:09  更:2021-08-27 11:44:32 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/31 1:46:14-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码