阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

编写equals方法

294次阅读
没有评论

共计 3729 个字符,预计需要花费 10 分钟才能阅读完成。

我们知道 List 是一种有序链表:List内部按照放入元素的先后顺序存放,并且每个元素都可以通过索引确定自己的位置。

List还提供了 boolean contains(Object o) 方法来判断 List 是否包含某个指定元素。此外,int indexOf(Object o)方法可以返回某个元素的索引,如果元素不存在,就返回-1

我们来看一个例子:

import java.util.List;

public class Main {public static void main(String[] args) {List<String> list = List.of("A", "B", "C");
        System.out.println(list.contains("C")); // true
        System.out.println(list.contains("X")); // false
        System.out.println(list.indexOf("C")); // 2
        System.out.println(list.indexOf("X")); // -1
    }
}

这里我们注意一个问题,我们往 List 中添加的 "C" 和调用 contains("C") 传入的 "C" 是不是同一个实例?

如果这两个 "C" 不是同一个实例,这段代码是否还能得到正确的结果?我们可以改写一下代码测试一下:

import java.util.List;

public class Main {public static void main(String[] args) {List<String> list = List.of("A", "B", "C");
        System.out.println(list.contains(new String("C"))); // true or false?
        System.out.println(list.indexOf(new String("C"))); // 2 or -1?
    }
}

因为我们传入的是new String("C"),所以一定是不同的实例。结果仍然符合预期,这是为什么呢?

因为 List 内部并不是通过 == 判断两个元素是否相等,而是使用 equals() 方法判断两个元素是否相等,例如 contains() 方法可以实现如下:

public class ArrayList {Object[] elementData;
    public boolean contains(Object o) {for (int i = 0; i < elementData.length; i++) {if (o.equals(elementData[i])) {return true;
            }
        }
        return false;
    }
}

因此,要正确使用 Listcontains()indexOf()这些方法,放入的实例必须正确覆写 equals() 方法,否则,放进去的实例,查找不到。我们之所以能正常放入 StringInteger 这些对象,是因为 Java 标准库定义的这些类已经正确实现了 equals() 方法。

我们以 Person 对象为例,测试一下:

import java.util.List;

public class Main {public static void main(String[] args) {
        List<Person> list = List.of(new Person("Xiao Ming"),
            new Person("Xiao Hong"),
            new Person("Bob")
        );
        System.out.println(list.contains(new Person("Bob"))); // false
    }
}

class Person {
    String name;
    public Person(String name) {this.name = name;
    }
}

不出意外,虽然放入了 new Person("Bob"),但是用另一个new Person("Bob") 查询不到,原因就是 Person 类没有覆写 equals() 方法。

编写 equals

如何正确编写 equals() 方法?equals()方法要求我们必须满足以下条件:

  • 自反性(Reflexive):对于非 nullx来说,x.equals(x)必须返回true
  • 对称性(Symmetric):对于非 nullxy 来说,如果 x.equals(y)true,则 y.equals(x) 也必须为true
  • 传递性(Transitive):对于非 nullxyz 来说,如果 x.equals(y)truey.equals(z)也为 true,那么x.equals(z) 也必须为true
  • 一致性(Consistent):对于非 nullxy 来说,只要 xy状态不变,则 x.equals(y) 总是一致地返回 true 或者false
  • null 的比较:即 x.equals(null) 永远返回false

上述规则看上去似乎非常复杂,但其实代码实现 equals() 方法是很简单的,我们以 Person 类为例:

public class Person {public String name;
    public int age;
}

首先,我们要定义“相等”的逻辑含义。对于 Person 类,如果 name 相等,并且 age 相等,我们就认为两个 Person 实例相等。

因此,编写 equals() 方法如下:

public boolean equals(Object o) {if (o instanceof Person p) {return this.name.equals(p.name) && this.age == p.age;
    }
    return false;
}

对于引用字段比较,我们使用equals(),对于基本类型字段的比较,我们使用==

如果 this.namenull,那么 equals() 方法会报错,因此,需要继续改写如下:

public boolean equals(Object o) {if (o instanceof Person p) {boolean nameEquals = false;
        if (this.name == null && p.name == null) {nameEquals = true;
        }
        if (this.name != null) {nameEquals = this.name.equals(p.name);
        }
        return nameEquals && this.age == p.age;
    }
    return false;
}

如果 Person 有好几个引用类型的字段,上面的写法就太复杂了。要简化引用类型的比较,我们使用 Objects.equals() 静态方法:

public boolean equals(Object o) {if (o instanceof Person p) {return Objects.equals(this.name, p.name) && this.age == p.age;
    }
    return false;
}

因此,我们总结一下 equals() 方法的正确编写方法:

  1. 先确定实例“相等”的逻辑,即哪些字段相等,就认为实例相等;
  2. instanceof 判断传入的待比较的 Object 是不是当前类型,如果是,继续比较,否则,返回false
  3. 对引用类型用 Objects.equals() 比较,对基本类型直接用 == 比较。

使用 Objects.equals() 比较两个引用类型是否相等的目的是省去了判断 null 的麻烦。两个引用类型都是 null 时它们也是相等的。

如果不调用 Listcontains()indexOf()这些方法,那么放入的元素就不需要实现 equals() 方法。

练习

给 Person 类增加 equals 方法,使得调用 indexOf()方法返回正常:

import java.util.List;
import java.util.Objects;

public class Main {public static void main(String[] args) {
        List<Person> list = List.of(new Person("Xiao", "Ming", 18),
            new Person("Xiao", "Hong", 25),
            new Person("Bob", "Smith", 20)
        );
        boolean exist = list.contains(new Person("Bob", "Smith", 20));
        System.out.println(exist ? "测试成功!" : "测试失败!");
    }
}

class Person {
    String firstName;
    String lastName;
    int age;
    public Person(String firstName, String lastName, int age) {this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
    }
}

下载练习

小结

List 中查找元素时,List的实现类通过元素的 equals() 方法比较两个元素是否相等,因此,放入的元素必须正确覆写 equals() 方法,Java 标准库提供的 StringInteger 等已经覆写了 equals() 方法;

编写 equals() 方法可借助 Objects.equals() 判断。

如果不在 List 中查找元素,就不必覆写 equals() 方法。

正文完
星哥玩云-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2024-08-05发表,共计3729字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中

星哥玩云

星哥玩云
星哥玩云
分享互联网知识
用户数
4
文章数
19351
评论数
4
阅读量
7979003
文章搜索
热门文章
星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛 NAS-6:抖音视频同步工具,视频下载自动下载保存 前言 各位玩 NAS 的朋友好,我是星哥!...
星哥带你玩飞牛NAS-3:安装飞牛NAS后的很有必要的操作

星哥带你玩飞牛NAS-3:安装飞牛NAS后的很有必要的操作

星哥带你玩飞牛 NAS-3:安装飞牛 NAS 后的很有必要的操作 前言 如果你已经有了飞牛 NAS 系统,之前...
我把用了20年的360安全卫士卸载了

我把用了20年的360安全卫士卸载了

我把用了 20 年的 360 安全卫士卸载了 是的,正如标题你看到的。 原因 偷摸安装自家的软件 莫名其妙安装...
再见zabbix!轻量级自建服务器监控神器在Linux 的完整部署指南

再见zabbix!轻量级自建服务器监控神器在Linux 的完整部署指南

再见 zabbix!轻量级自建服务器监控神器在 Linux 的完整部署指南 在日常运维中,服务器监控是绕不开的...
飞牛NAS中安装Navidrome音乐文件中文标签乱码问题解决、安装FntermX终端

飞牛NAS中安装Navidrome音乐文件中文标签乱码问题解决、安装FntermX终端

飞牛 NAS 中安装 Navidrome 音乐文件中文标签乱码问题解决、安装 FntermX 终端 问题背景 ...
阿里云CDN
阿里云CDN-提高用户访问的响应速度和成功率
随机文章
星哥带你玩飞牛NAS硬件02:某鱼6张左右就可拿下5盘位的飞牛圣体NAS

星哥带你玩飞牛NAS硬件02:某鱼6张左右就可拿下5盘位的飞牛圣体NAS

星哥带你玩飞牛 NAS 硬件 02:某鱼 6 张左右就可拿下 5 盘位的飞牛圣体 NAS 前言 大家好,我是星...
星哥带你玩飞牛 NAS-9:全能网盘搜索工具 13 种云盘一键搞定!

星哥带你玩飞牛 NAS-9:全能网盘搜索工具 13 种云盘一键搞定!

星哥带你玩飞牛 NAS-9:全能网盘搜索工具 13 种云盘一键搞定! 前言 作为 NAS 玩家,你是否总被这些...
零成本上线!用 Hugging Face免费服务器+Docker 快速部署HertzBeat 监控平台

零成本上线!用 Hugging Face免费服务器+Docker 快速部署HertzBeat 监控平台

零成本上线!用 Hugging Face 免费服务器 +Docker 快速部署 HertzBeat 监控平台 ...
星哥带你玩飞牛NAS-8:有了NAS你可以干什么?软件汇总篇

星哥带你玩飞牛NAS-8:有了NAS你可以干什么?软件汇总篇

星哥带你玩飞牛 NAS-8:有了 NAS 你可以干什么?软件汇总篇 前言 哈喽各位玩友!我是是星哥,不少朋友私...
一句话生成拓扑图!AI+Draw.io 封神开源组合,工具让你的效率爆炸

一句话生成拓扑图!AI+Draw.io 封神开源组合,工具让你的效率爆炸

一句话生成拓扑图!AI+Draw.io 封神开源组合,工具让你的效率爆炸 前言 作为天天跟架构图、拓扑图死磕的...

免费图片视频管理工具让灵感库告别混乱

一言一句话
-「
手气不错
4盘位、4K输出、J3455、遥控,NAS硬件入门性价比之王

4盘位、4K输出、J3455、遥控,NAS硬件入门性价比之王

  4 盘位、4K 输出、J3455、遥控,NAS 硬件入门性价比之王 开篇 在 NAS 市场中,威...
Prometheus:监控系统的部署与指标收集

Prometheus:监控系统的部署与指标收集

Prometheus:监控系统的部署与指标收集 在云原生体系中,Prometheus 已成为最主流的监控与报警...
三大开源投屏神器横评:QtScrcpy、scrcpy、escrcpy 谁才是跨平台控制 Android 的最优解?

三大开源投屏神器横评:QtScrcpy、scrcpy、escrcpy 谁才是跨平台控制 Android 的最优解?

  三大开源投屏神器横评:QtScrcpy、scrcpy、escrcpy 谁才是跨平台控制 Andr...
支付宝、淘宝、闲鱼又双叕崩了,Cloudflare也瘫了连监控都挂,根因藏在哪?

支付宝、淘宝、闲鱼又双叕崩了,Cloudflare也瘫了连监控都挂,根因藏在哪?

支付宝、淘宝、闲鱼又双叕崩了,Cloudflare 也瘫了连监控都挂,根因藏在哪? 最近两天的互联网堪称“故障...
国产开源公众号AI知识库 Agent:突破未认证号限制,一键搞定自动回复,重构运营效率

国产开源公众号AI知识库 Agent:突破未认证号限制,一键搞定自动回复,重构运营效率

国产开源公众号 AI 知识库 Agent:突破未认证号限制,一键搞定自动回复,重构运营效率 大家好,我是星哥,...