Java对象的比较——equals方法,Comparable接口,Comparator接口

Java对象的比较——equals方法,Comparable接口,Comparator接口

  • 1. equals方法
  • 2. Comparable接口
  • 3. Comparator接口

1. equals方法

在判断两个整数是否相同时,我们可以使用以下方式:

System.out.println(1 == 2);
System.out.println(1 == 1);

如果输出true,则说明这两个整数相同;如果输出false,则说明这两个整数不相同

那么,如果将==用于判断两个对象,又会是怎样的情况呢?我们直接敲代码来看看!

public class Dog {
    private String name;
    private int age;

    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
public class Test1 {
    public static void main(String[] args) {
        Dog dog1 = new Dog("zhangsan",1);
        Dog dog2 = new Dog("zhangsan",1);
        System.out.println(dog1 == dog2);
    }
}

运行结果:
在这里插入图片描述
当运行这段代码时,我们发现输出的是false。明明dog1和dog2的属性都一模一样,为什么输出false呢?莫慌,且听我慢慢道来!
在这里插入图片描述
画图来分析,dog1和dog2中存放的值并不相同,因此dog1 != dog2。而你之所以认为输出的应该是true,是因为你认为,两个对象的属性完全一致,所以dog1 == dog2.其实,并不是这样的,==判断的并不是对象的属性是否一致,而是判断两个引用指向的是否是同一个对象!

在Java中,一切皆对象。而dog1和dog2是对象的引用。==判断的并不是对象的属性是否一致,而是判断两个引用指向的是否是同一个对象!

因此,只有当两个引用指向同一对象才返回true;而如果两个引用指向不同的对象,即使两个对象的属性完全相同,返回依旧是false!

总结:

  • 如果==左右两边是基本数据类型变量,比较的是变量中的值是否相同
  • 如果==左右两边是引用数据类型变量,比较的是引用变量中的值是否相同,而引用变量中存放的是对象的地址,所以比较的就是引用变量中存放的地址是否相同(即判断两个引用指向的是否是同一个对象!)

理解了这一点,学习equals方法就简单多了!我们先来看看equals方法的原型,equals方法在Object类中
在这里插入图片描述
有没有发现,equals方法,返回正是刚刚所讲的内容,说明equals方法默认就是判断两个引用指向的是否是同一个对象!

注意: equals方法的返回值是boolean类型!

再来尝试运行这段代码:

public class Test1 {
    public static void main(String[] args) {
        Dog dog1 = new Dog("zhangsan",1);
        Dog dog2 = new Dog("zhangsan",1);
        System.out.println(dog1.equals(dog2));
    }
}

运行结果依旧是false


那么,问题来了,如果我们想通过比较两个对象的属性是否相等,如果相等,从逻辑上就说明他们就是相等的,该怎么办呢?

我们知道Object类是一切类的父类,而equals方法在Object类中,是不是就通过可以重写equals方法,从而达到自定义比较方式的目的!

下面就演示重写equals方法,规则:如果两个对象的属性完全一致,则返回true;否则放回false

public class Dog {
    private String name;
    private int age;

    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public boolean equals(Object obj) {
        Dog tmp = (Dog)obj;
        return this.name.equals(tmp.name) && this.age == tmp.age;
    }
}
public class Test1 {
    public static void main(String[] args) {
        Dog dog1 = new Dog("zhangsan",1);
        Dog dog2 = new Dog("zhangsan",1);
        System.out.println(dog1.equals(dog2));
    }
}

这时运行代码,输出的就是true!

这时,有人可能就有疑问了,为什么名字比较要用equals方法,其实这里的equals方法并非Dog类中的equals方法,而是String类中的equals方法,用来判断两个字符串是否相等

String类中的equals方法原型:
在这里插入图片描述
另外,编译器可以帮我们自动生成equals方法,第一步按住alt+insert
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
一路点下去,就可以生成equals方法,除此之外,还生成了hashCode方法
在这里插入图片描述

2. Comparable接口

通过重写equals方法,我们可以从逻辑上去判断两个对象是否相同。但是如果要去比较两个对象的大小,又该怎么去比较呢?对象的属性那么多,通过什么属性去比较呢?这时就需要讲到Comparable接口!

在这里插入图片描述
当我们在Student类后加上Comparable接口时,发现会报错,这是为什么呢?我们按住CTRL键,再鼠标左击Comparable,进入源码
在这里插入图片描述
我们发现,Comparable接口中还有个compareTo抽象方法,因此当我们在Student类要实现这个方法,除此之外,Comparable接口后还有个,这是泛型,我们需要比较什么类型的对象,就在实现接口时把T改成什么
在这里插入图片描述
由编译器自动生成实现compareTo方法的代码后:
在这里插入图片描述
我们就需要书写compareTo的方法体,那怎么书写呢?你想根据哪个对象的属性进行比较,就怎么书写。


比如,我现在想根据对象的年龄进行比较
在这里插入图片描述
下面进行测试:

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

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

    @Override
    public int compareTo(Student o) {
        return this.age-o.age;
    }
}
public class Test {
    public static void main(String[] args) {
        Student student1 = new Student("zhangsan", 18);
        Student student2 = new Student("lisi", 10);
        System.out.println(student1.compareTo(student2));
    }
}

运行结果:
在这里插入图片描述
返回的是一个正数,说明根据年龄比较,student1大于student2


再比如,我不想根据年龄比较了,我想根据姓名比较,这时就不能用简单的this.name-o.name了,因为name是一个String类型,不能通过这样的方式进行比较
在这里插入图片描述
方法里的compareTo指并不是在Student类中具体实现的这个compareTo,而是String类中的conpareTo,用于字符串的比较,它的底层是和C语言的strcmp是一样的,返回的是两个字母的ASCII 码的差值
String类中的compareTo方法


那么,问题来了,前面我们比较的只有两个对象,如果需要比较多个对象呢,改怎么办呢?这时候就需要用到数组来存放对象,用Arrays里的排序方法进行排序

当我们写下以下代码:

public class Student {
    private String name;
    private int age;

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class Test {
    public static void main(String[] args) {
        Student[] students = new Student[]{
                new Student("zhangsan",18),
                new Student("lisi", 10),
                new Student("wangwu", 20)};
        System.out.println("排序前:" + Arrays.toString(students));
        Arrays.sort(students);
        System.out.println("排序后:" + Arrays.toString(students));
    }
}

当我们运行代码后,发现会出现异常
在这里插入图片描述
在这里插入图片描述

这里需要用到强转,而Student类并没有去实现Comparable接口,因此会导致强转失败!这就是异常所在!所以,我们需要在Student类中实现Comparable接口

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

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

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

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

在这里插入图片描述

当我们再次去运行代码时,会发现排序后是根据年龄从小到大排序的,这和Student类中实现的compareTo难道有关系?答案是正确的

假如我们想按照年龄从大到小去排序,就做出如下更改
在这里插入图片描述
调换顺序即可,再次运行代码:
在这里插入图片描述


假如想根据年龄去比呢?

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

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

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

    @Override
    public int compareTo(Student o) {
        return this.name.compareTo(o.name);
    }
}
public class Test1 {
    public static void main(String[] args) {
        Student[] students = new Student[]{
                new Student("zhangsan",10),
                new Student("lisi", 18),
                new Student("wangwu", 9)};
        System.out.println("排序前:" + Arrays.toString(students));
        Arrays.sort(students);
        System.out.println("排序后:" + Arrays.toString(students));
    }
}

3. Comparator接口

在前面使用Comparable接口来实现对象的比较时,我们不难发现,这个比较方法并不灵活,只能固定地通过一种方式去比较,那么如果我们有的时候想通过年龄比较,有的时候想通过姓名比较,这个接口就无法实现了,这时地解决办法就是,换一个接口,用Comparator接口来实现!

这时,我们分别写两个类——NameComparator和AgeComparator,代表分别通过姓名比较和通过年龄比较。并且这两个类都要实现Comparator接口!

import java.util.Comparator;

public class NameComparator implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        return o1.getName().compareTo(o2.getName());//姓名通过compareTo方法进行比较
    }
}
import java.util.Comparator;

public class AgeComparator implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        return o1.getAge() - o2.getAge();
    }
}

注意: 实现Comparator接口需要重写的时compare方法,不是compareTo方法!实现Comparable接口重写的才是compareTo方法!

学生类如下:

public class Student {
    private String name;
    private int age;

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

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

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

测试一下代码:

import java.util.Arrays;

public class Test {
    public static void main(String[] args) {
        //两个对象的比较
        AgeComparator ageComparator = new AgeComparator();
        NameComparator nameComparator = new NameComparator();
        Student student1 = new Student("zhangsan", 20);
        Student student2 = new Student("lisi", 30);
        System.out.println("根据年龄比:" + ageComparator.compare(student1, student2));
        System.out.println("根据姓名比:" + nameComparator.compare(student1, student2));

        //一组对象的比较
        Student[] students = new Student[]{
                new Student("zhangsan", 10),
                new Student("lisi", 18),
                new Student("wangwu", 9)};
        System.out.println("排序前:" + Arrays.toString(students));
        Arrays.sort(students, ageComparator);
        System.out.println("根据年龄排序后:" + Arrays.toString(students));
        Arrays.sort(students, nameComparator);
        System.out.println("根据姓名排序后:" + Arrays.toString(students));
    }
}

分析两个对象的比较:
在这里插入图片描述
这里,我们创建了一个AgeComparator和一个NameComparator对象,用来表示是通过年龄比较还是通过姓名比较。注意最后两个Student类对象的比较方法

分析一组对象的比较:
在这里插入图片描述
我们发现,在用Arrays.sort排序时,里面还加上了一个ageComparator对象(或NameComparator对象),通过这个对象,可以控制通过什么方式去比较,当使用Arrays对数组进行排序时,就会调用ageComparator(或NameComparator)里的compare方法依次将数组里的对象进行比较

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


请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/667574.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【传知代码】基于知识引导提示的因果概念提取(论文复现)

前言&#xff1a;在当今信息爆炸的时代&#xff0c;我们被海量的数据所包围&#xff0c;然而&#xff0c;这些数据中的真正价值往往隐藏在深层的因果关系之中。无论是科学研究、商业决策&#xff0c;还是日常生活中的选择&#xff0c;理解并准确把握事物之间的因果关系&#xf…

Nginx 文件下载 限速设置 限制访问频率 下载速率 并发连接数 简单实用教程

1 没有限速之前 2 nginx配置 #增加如下配置 limit_conn_zone $binary_remote_addr zoneaddr:10m; location / {limit_conn addr 1; #按照来源&#xff0c;限制每个IP 的连接数为1limit_rate_after 1000k;不限速下载的数据量limit_rate 100k; #限制最大传输速率root /data/log…

Lesson6--排序(初级数据结构完结篇)

【本节目标】 1. 排序的概念及其运用 2. 常见排序算法的实现 3. 排序算法复杂度及稳定性分析 1.排序的概念及其运用 1.1排序的概念 排序 &#xff1a;所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来…

随身wifi和手机流量卡,你知道该怎么选吗?

网络已经成为我们这个时代的代名词&#xff01; 那么&#xff0c;在上网的时代&#xff0c;我们有很多问题都要考虑&#xff0c;比如如何选择上网方式&#xff0c;是选择一张流量卡&#xff0c;还是一个随身WIFI&#xff1f; 听小编一句劝&#xff0c;先不要着急买&#xff0c…

2024年中国CRM行业发展方向和前景 | 《连接型CRM》文章精选

01、创新突破&#xff0c;技术为本 中国经济发展处于增速换挡期&#xff0c;企业数字化需求旺盛&#xff0c;同时云计算、大数据、物联网、区块链、5G等新技术的发展&#xff0c;为CRM系统的应用与发展提供了更多的机遇和可能。 近些年来&#xff0c;技术的发展对CRM的重要性…

打造你的专属Vue组件:超实用“高级筛选弹窗组件“实战

打造你的专属Vue组件&#xff1a;超实用“高级筛选弹窗组件“实战 在现代前端开发中&#xff0c;组件化思想是提高开发效率、维护性和代码复用性的关键。本文将通过一个实例——创建一个自定义的“高级筛选”弹窗组件&#xff0c;来展示如何在Vue框架下利用Composition API和E…

AI图书推荐:使用GPT-4和ChatGPT开发AI应用APP

这本书是面向想要学习如何使用大型语言模型构建应用程序的 Python 开发人员的全面指南。作者 Olivier Caelen 和 Marie-Alice Blete 涵盖了 GPT-4 和 ChatGPT 的主要特征和好处&#xff0c;并解释了它们的工作原理。您还将获得使用 GPT-4 和 ChatGPT Python 库开发应用程序的逐…

解决3D模型变黑及贴图不显示的问题---模大狮模型网

在3D建模和渲染过程中&#xff0c;模型变黑或贴图不显示是常见的挑战之一。这不仅影响了模型的视觉效果&#xff0c;还可能导致后续的工作流程受阻。本文将针对这两个问题&#xff0c;提供详细的解决方法和步骤&#xff0c;帮助读者快速有效地解决问题。 一、检查并调整光照设置…

【SpringBoot】怎么在一个大的SpringBoot项目中创建多个小的SpringBoot项目,从而形成子父依赖

父子项目工程创建 步骤 先创建父项目 具体操作步骤请看本文章&#xff1a;使用maven工程创建spring boot项目 创建子项目 file- project structure module–new module 剩下步骤请看创建父工程时的操作使用maven工程创建spring boot项目 应用 确认即可 之后创建启动类…

【实战JVM】-实战篇-05-内存泄漏及分析

【实战JVM】-实战篇-05-内存泄漏及分析 1 内存溢出和内存泄漏1.1 常见场景1.2 解决内存溢出的方法1.2.1 发现问题1.2.1.1 top1.2.1.2 ViusalVM1.2.1.3 arthas1.2.1.4 PrometheusGrafana 1.2.2 堆内存状况对比1.2.3 内存泄漏原因-代码中1.2.3.1 equals()-hashCode()1.2.3.2 内部…

倪师哲学。能让我好,我就接受

还有有些人更搞笑的是&#xff0c;把自己的行为啊&#xff0c;建立在别人的基础之上&#xff0c;如果那个人么样对我&#xff0c;我肯定能怎么样对这个人。 生而为人呐&#xff0c;你是一个独立的人&#xff0c;不要去总是拿着各种各样的前提&#xff0c;来限制了自己个人的成长…

工作流 Activiti7 初始

文章目录 ☃️1.1 Activiti 介绍☃️1.2 Activiti 开发流程☃️1.3 BPMN 2.0 规范是什么☃️1.4 BPMN 2.0 基本流程符号❄️❄️1.4.1 事件 Event❄️❄️1.4.2 活动❄️❄️1.4.3 网关 Gateway ☃️1.5 Activiti API 服务接口❄️❄️1.5.1 核心Service接口及其获取 ☃️1.1 A…

单片机按键处理模块

一 介绍 1.key_board用于单片机中的小巧多功能按键支持&#xff0c;软件采用了分层的思想&#xff0c;并且做到了与平台无关&#xff0c;用户只需要提供按键的基本信息和读写io电平的函数即可&#xff0c;非常方便移植&#xff0c;同时支持多个矩阵键盘及多个单io控制键盘。 …

儿童节快乐!探索图形化编程桌面的“童年”成长之路

在这个充满童真与快乐的儿童节&#xff0c;我要向在CSDN平台上努力拼搏的每一位朋友&#xff0c;送上我最热切、最深情的祝福&#xff01;愿你们心中那份孩童般的纯真与对世界无尽的好奇永不褪色&#xff0c;愿你们的人生道路如同这个美好的节日&#xff0c;流光溢彩、欢乐永恒…

nginx搭建简单负载均衡demo(springboot)

目录 1 安装nignx 1.1 执行 brew install nginx 命令&#xff08;如果没安装brew可百度搜索如何安装brew下载工具。类似linux的yum命令工具&#xff09;。 1.2 安装完成会有如下提示&#xff1a;可以查看nginx的配置文件目录。 1.3 执行 brew services start nginx 命令启动…

【qt】多窗口开发

多窗口开发 一.应用场景二.嵌入的窗口1.设计Widget窗口2.创建窗口3.添加窗口4.总代码 三.独立的窗口1.创建窗口2.显示窗口 四.总结 一.应用场景 多窗口,顾名思义,有多个窗口可以供我们进行操作! 截个小图,你应该就知道了 OK,话不多说,直接开干,先来设计我们的主窗口 需要蔬菜…

Mysql基础教程(13):GROUP BY

MySQL GROUP BY 【 GROUP BY】 子句用于将结果集根据指定的字段或者表达式进行分组。 有时候&#xff0c;我们需要将结果集按照某个维度进行汇总。这在统计数据的时候经常用到&#xff0c;考虑以下的场景&#xff1a; 按班级求取平均成绩。按学生汇总某个人的总分。按年或者…

如何让Google收录网站?

Google收录网站的前提条件是确保网站可以公开访问&#xff0c;并且页面加载速度需要快&#xff0c;这样Google爬虫才可以访问到你的网站&#xff0c;并且索引你网站中的内容。实现了上面的前提条件&#xff0c;可以通过优化数据结构、创建站点地图、使用Google Search Console、…

postgressql——PGPROC XLOG(6)

PGPROC相关结构 在共享内存中,核心数据结构围绕PROC_HDR指向的两个list:PROC和XACT PRCO内存连续,维护链表结构方便申请释放,对应每个后台服务进程,PID为OS标识、PGPROCNO为内部标识 XACT内存连续,维护快照需要的xmin和xid,XACT从PROC拆出来是为了更高的cache line命中…

华为诺亚等发布MagicDrive3D:自动驾驶街景中任意视图渲染的可控3D生成

文章链接&#xff1a;https://arxiv.org/pdf/2405.14475 项目链接&#xff1a;https://flymin.github.io/magicdrive3d 虽然可控生成模型在图像和视频方面取得了显著成功&#xff0c;但在自动驾驶等无限场景中&#xff0c;高质量的3D场景生成模型仍然发展不足&#xff0c;主…