Java ==,equals()与hashcode()的使用
作者:按时交作业 发布时间:2022-02-24 07:06:28
==,equals()与hashcode()
"=="
在讲解之前,我们是怎么接触到==
的,我们在作比较时经常用到 ==
, 这个符号常用作基本类的数据。
==
运算符通常用于比较基本数据类型(如int、long、double等)和枚举类型的值是否相等,而对于对象类型,==
运算符用于比较它们的引用是否相等,即它们是否指向同一个对象。
思考一个问题,为什么我们一般不使用==来比较对象,如果两个对象的引用相同,即它们指向同一个对象,那么使用==
运算符比较它们的引用会返回true,因为它们确实指向同一个对象。在这种情况下,两个对象的内容自然是相同的,因为它们是同一个对象。
那为啥我们不直接使用==
去比较对象, 而要麻烦的使用其它的办法,
因为: 我们比较对象不是想看它的引用是否相同,而是里面的属性是否相同
在这种情况下,使用==
运算符比较两个对象的引用并不能判断它们的内容是否相同,因为两个不同的对象可能具有相同的属性值,但它们的引用不同。因此,在比较对象是否相同时,应该根据对象的语义来实现equals()
方法,而不是使用==
运算符。
public class Main {
public static void main(String[] args) {
int a = 5;
int b = 5;
int c = 6;
System.out.println(a==b); // true
System.out.println(a==c); // false
Person p1 = new Person("hhh", 15);
Person p2 = new Person("hhh", 15);
// 属性相同,但引用不同。按照常理应该被认作是相同的。实际却是不同的
System.out.println(p1 == p2); // false
}
}
class Person{
String name;
Integer age;
Person(String name, Integer age){
this.age = age;
this.name = name;
}
}
equals()方法
equals()
方法是Object
类中的方法,它仅仅是地址的比较,因此只能比较两个类是否是同一个类,而不能比较属性是否相同,因此,我们比较对象一般都会重写equals()
方法
public class Point {
private int x;
private int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
// getters and setters
@Override
public boolean equals(Object obj) {
return (this == obj);
}
}
如果一个类的属性包含基本数据类型和引用数据类型,那么这些属性值相同,但它们的引用地址也不一定相同。此时,必须重写equals()
方法,根据对象的语义来进行比较,才能得到正确的结果。
列表中的使用
在Java中,列表类(如List
)通常用来存储一组对象,当我们需要在列表中查找某个对象时,通常需要调用列表的contains()
方法或indexOf()
方法。这些方法会使用对象的equals()
方法来比较对象是否相等。
它是通过比较对象的属性值从而去列表中查找。
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
System.out.println(list.indexOf(new String("C"))); // 2
}
}
在hash结构的表中使用
map
是一种键值对的映射表,map
使用一个很大的数组来存储value
,然后通过key
的hash
值来查找value
的索引
map
其实可以看作一个数组,key
的hashcode()
值可以作为数组的索引,然后根据索引去查找value
。
那究竟在什么地方使用equals()
呢?
当我们输入key
的时候,map
需要将传入的key
与当前数组中存入的key
相比较。
public class Main {
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<>();
map.put(new String("sss"),"hhh");
map.put(new String("sss"), "aaa");
// map里只有一个数据,因为String重写了equals方法,所以key属性值一样的会被覆盖
System.out.println(map.size());
HashMap<Person, String> map1 = new HashMap<>();
map1.put(new Person("xiao",15), "ok");
map1.put(new Person("xiao",15), "hao");
// map1里有两个数据,明明key是一样的,却没有覆盖
System.out.println(map1.size());
}
}
class Person{
String name;
int age;
Person(String name, int age){
this.name = name;
this.age = age;
}
}
添加重复的键值对:由于
Map
将不同的对象视为不同的键,如果我们使用相同类型的不同对象作为键,即使它们的属性值相同,Map
也会将它们视为不同的键,从而导致添加重复的键值对。无法查找键值对:由于
Map
将不同的对象视为不同的键,如果我们使用一个对象作为键添加了一个键值对,而后又使用另一个对象作为键查找该键值对,那么Map
会认为这两个对象是不同的键,因此无法找到对应的键值对。
因此,我们需要正确重写equals()
方法。
hashcode()方法
HashMap
为什么能通过key
直接计算出value
存储的索引。相同的key
对象(使用equals()
判断时返回true
)必须要计算出相同的索引,否则,相同的key
每次取出的value
就不一定对。
map
是通过hashcode()
去寻找value
数组索引的,所以还需要为对象覆写hashcode()
方法。
public class Main {
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<>();
map.put(new String("sss"),"hhh");
map.put(new String("sss"), "aaa");
// map里只有一个数据,因为String重写了equals方法和hashcode方法,所以key属性值一样的会被覆盖
System.out.println(map.size()); // 1
HashMap<Person, String> map1 = new HashMap<>();
Person p1 = new Person("xiao", 15);
map1.put(p1, "ok");
Person p2 = new Person("xiao", 15);
map1.put(p2, "hao");
// map现在也只有一个数据了,因为两个相同属性的对象被视作同一个
System.out.println(map1.size()); // 1
}
}
class Person{
String name;
int age;
Person(String name, int age){
this.name = name;
this.age = age;
}
// 覆写equals方法
@Override
public boolean equals(Object o) {
if (o instanceof Person p) {
return this.name.equals(p.name) && this.age == p.age;
}
return false;
}
// 覆写hashcode方法
@Override
public int hashCode() {
int h = 0;
h = 31 * h + name.hashCode();
h = 31 * h + age;
return h;
}
}
map
的put
方法里是用到了hash
值的,因此不正确重写hash
值而正确重写equals
方法是没用的。
小节
比较基本数据类型或对象地址用==
, 比较对象地址使用Object
的equals
方法, 比较对象属性值使用覆写之后的equals()
.
对于list
集合数据类型,本质是数组,当存储对象数据类型的值时,重写equals方法
才能使用contains()
等需要使用equals
进行比较的方法。
对于hash table类型的数据,必须覆写equals与hashcode方法,才能正常工作。
来源:https://juejin.cn/post/7235118809605292087


猜你喜欢
- 一般来说,在更新DataTable或是DataSet时,如果不采用SqlParameter,那么当输入的Sql语句出现歧义时,如字符串中含有
- Java 编程语言中时间的处理类有 Date类与 Calendar类。目前官方不推荐使用 Date类,因为其不利于国际化;而是推荐使用 Ca
- 本文实例讲述了Android使用onCreateOptionsMenu()创建菜单Menu的方法。分享给大家供大家参考,具体如下:Andro
- 1 SharedPreferences 介绍SharedPreferences是使用键值对的方式来存储数据的SharedPreference
- 消息发送过程消息的发送可能会经过 * 、序列化、分区器等过程。消息发送的主要涉及两个线程,分别为main线程和sender线程。如图所示,主
- Java中提供了大数字(超过16位有效位)的操作类,即 java.math.BinInteger 类和 java.math.BigDecim
- 本文实例为大家分享了OpenGL绘制三次Bezier曲线的具体代码,供大家参考,具体内容如下计算公式:运行结果:代码如下:#include&
- 非常简单的一段设置安卓全屏的代码public class MainActivity extends Activity { &nbs
- protected 来谈谈protected访问权限问题。看下面示例1:Test.javaclass MyObject {}public c
- 我们在编写Web应用时,经常需要对页面做一些安全控制,比如:对于没有访问权限的用户需要转到登录表单页面。要实现访问控制的方法多种多样,可以通
- maven scope provided和runtime例子maven常用的scope有compile,provided,runtime,t
- C#中的表格控件只有一个,那就是datagridview,不像QT中可以用QTableview,QTableWidget。新手拿到datag
- 问题描述springcloud项目部署或调试时,占用的内存特别多。当部署到服务器上去后,有可能导致服务器内存占用过多而崩溃。解决方案&nbs
- 多线程的创建,方式一:继承于Thread类1.创建一个继承于Thread类的子类2.重写Thread类的run()--->将此线程执行
- 什么是 Nacos Config在分布式系统中,由于服务数量巨多,为了方便服务 配置文件统一管理,实时更新,所以需要分布式配置中心组件。Sp
- 实现方式和继承方式有什么区别呢?*区别:*继承Thread:线程代码存放在Thread子类run方法中*实现Runnable:线程代码存放在
- java 中模式匹配算法-KMP算法实例详解朴素模式匹配算法的最大问题就是太低效了。于是三位前辈发表了一种KMP算法,其中三个字母分别是这三
- 需求:接口是否可继承接口?抽象类是否可实现(implements)接口?抽象类是否可继承实体类(concrete class)?抽象类中是否
- 1.根据入参带分页参数进行sql查询分页 Criteria criteria = n
- 前言xxljob 是采用 java 开发的开源的任务调度系统,架构上分为调度管理器、执行器,目前除了官方提供的 java 执行器外,也有 g