首页 > 其他分享 >为什么重写equals方法时一定要重写hashCode方法

为什么重写equals方法时一定要重写hashCode方法

时间:2023-10-29 20:33:26浏览次数:31  
标签:p1 Point 重写 equals hashCode 方法

为什么重写equals方法时一定要重写hashCode方法

在每个类中,在重写equals方法的时侯,一定要重写hashcode方法。

根据Object规范,规范约定:

  1. 如果两个对象通过equals方法比较是相等的,那么它们的hashCode方法结果值也是相等的。
  2. 如果两个对象通过equals方法比较是不相等的,那么不要求它们的hashCode方法结果值是相等的。
  3. 当在一个应用程序执行过程中, 如果equals方法比较中没有修改任何信息,那么在同一个对象上重复调用hashCode方法时,它必须始终返回相同的值。但如果从一个应用程序到了另一个应用程序,两个应用程序汇中调用hashCode方法的返回值可以是不一致的。

Object类中的默认的equals和hashCode方法:

  • equals:比较的是对象的内存地址是否相同(相当于==操作符);
  • hashCode:hashCode方法的返回值符合上述规范。

因此,当只重写equals方法,不重写hashCode时,违反规定:equals相等的对象必须具有相等的哈希码(因为hashCode的返回值还是按照Object类的规范:同一对象的hashCode值相同)。

如果不这样做,你的类违反了hashCode的通用约定,对于HashSet, HashMap, HashTable等基于hash值的类就会出现问题。

以HashMap为例,当集合要添加新的对象时,先调用这个对象的hashCode方法,得到对应的hashcode值,实际上在HashMap的具体实现中会用一个table保存已经存进去的对象的hashcode值,如果table中没有该hashcode值,它就可以直接存进去,不用再进行任何比较了;如果存在该hashcode值,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。
这样解决了向含有大量数据的集合中添加元素时,大量频繁的操作equals方法的问题。

下面举个例子说明:
创建一个Point类,有两个成员变量x和y,并重写了equals方法。

public class Point {
	private final int x, y;
public Point(int x, int y) {
    this.x = x;
    this.y = y;
}
@Override
public boolean equals(Object obj) {
    if (this == obj) return true;
    if (!(obj instanceof Point)) return false;
    Point point = (Point) obj;
    return x == point.x && y == point.y;
}
public static void main(String[] args) {
	Point p1 = new Point(1, 2);
	Point p2 = new Point(1, 2);
	System.out.println(p1.equals(p2));// true
	
	Map<Point, String> map = new HashMap<>();
	map.put(p1, "p1");
	System.out.println(map.get(p2)); // null
}

}

你可能觉得 map.get(p2) 应该返回字符串 p1, 但是却返回null, 这是因为Point类并没有重写hashCode方法,导致两个相等的实例p1和p2返回了不同的哈希码,违反了hashCode的约定,put方法把实例p1放到了一个哈希桶(hash bucket)中,但因为p2的哈希码不等于p1的哈希码,所以get方法会从其它哈希桶中去查找。

解决这个方法很简单,只需要重写Point类的hashCode方法。

	@Override
	public int hashCode() {
	    int result = Integer.hashCode(x);
	    result = 31 * result + Integer.hashCode(y);
	    return result;
	}
	public static void main(String[] args) {
		Point p1 = new Point(1, 2);
		Point p2 = new Point(1, 2);
		System.out.println(p1.equals(p2));// true
	Map<Point, String> map = new HashMap<>();
	map.put(p1, "p1");
	System.out.println(map.get(p2)); // p1
}

这次你会发现map.get(p2) 返回的就是字符串p1了, 因为hashCode这个方法会返回一个简单的确定性计算的结果,它的唯一的输入是 Point实例中的两个重要的属性x和y,所以显然相等的 Point实例具有相同的哈希码。

此外Objects 类有一个静态方法,它接受任意数量的对象并为它们返回一个哈希码。这个名为 hash 的方法可以 让你编写一行 hashCode 方法,其质量与根据这个项目中的上面编写的方法相当。

	@Override
	public int hashCode() {
		return Objects.hash(x, y);
	}

注意事项

  1. 当你写完 hashCode 方法后,请一定问一下自己是否满足相等的实例有相同的哈希码这一条件。
  2. hashCode中涉及到的属性应与equals中保持一致,不要试图从哈希码计算中排除重要的属性来提高性能。

总之,每次重写 equals 方法时都必须重写 hashCode 方法,否则程序将无法正常运行。你的 hashCode 方 法必须遵从 Object 类指定的常规约定,并且必须执行合理的工作,将不相等的哈希码分配给不相等的实例

原文链接:https://juejin.im/post/5e0226bb6fb9a0165936f44b

标签:p1,Point,重写,equals,hashCode,方法
From: https://www.cnblogs.com/sunny3158/p/17796397.html

相关文章

  • c++中重载、重写、隐藏的区别
    重载:同一个函数的不同表现形式。同一个类中;函数原型不同(函数名相同,参数列表即顺序、个数、类型不同);virtual关键字可有可无。 重写:继承关系中,派生类对基类同名函数有不同的表现形式。有继承关系的类;函数原型相同;基类成员函数必须声明为虚函数(virtual)。 隐藏:继......
  • Python中的equals用法介绍
      一、使用场景在Python中,equals(等于)经常用于比较两个对象是否相等。在Python中,使用==可以判断两个对象的值是否相等,使用is可以判断两个变量是否引用同一个对象。但是对于不同类型的对象,使用==方法可能会出现意想不到的结果。因此,在使用==判断两个对象是否相等时,需要注意以......
  • HashCode
    2023.10.241.开放定址法:基本思想是:当关键字key的哈希地址p=H(key)出现冲突时,以p为基础,产生另一个哈希地址p1,如果p1仍然冲突,再以p为基础,产生另一个哈希地址p2,…,直到找出一个不冲突的哈希地址pi,将相应元素存入其中。比如ThreadLocal再哈希法:这种方法是同时构造多个不同的哈希函数:H......
  • 重写和重载
    2023.10.181.classA{publicAfoo(){returnthis;}}classBextendsA{publicAfoo(){returnthis;}}classCextendsB{_______}可以填入哪个而不报错Apublicvoidfoo(){}Bpublicintfoo(){return1;}Cpubli......
  • 谷歌使用Jetpack Compose逐步重写Android 14,不会你还不知道吧?
    前言早在2019年,谷歌就推出了JetpackCompose,这是一种使用Kotlin开发原生安卓应用的编写方式,抛弃了常规基于XML的视图来设计应用UI,而是让开发者以声明方式创建设计。从那时起,谷歌就大力鼓励开发者在安卓应用中使用JetpackCompose,还使用JetpackCompose重构了其PlaySto......
  • 【有趣的小细节】在Java中native方法hashcode()默认是如何生成哈希码的?
    之前看其他文章说,hashcode是根据对象的内存地址生成的。但为了满足自己的好奇心,同时验证这个结论是否是真实的,我半个月前深究了一下。今天突然想起来这回事了,把结论记录一下。结论目前hashcode的方式有以下六种算法:HashCodeMode==0:由操作系统生成的一个随机数。HashCodeMode==1:基......
  • 一个 hashCode() 函数引发的​「​惨案」
    1、起因让我关注到这一点的起因是一道题:牛客网上的max-points-on-a-line(答题:https://www.nowcoder.com/practice/bfc691e0100441cdb8ec153f32540be2)题目是这么描述的:Givennpointsona2Dplane,findthemaximumnumberofpointsthatlieonthesamestraightline.大意就......
  • java基础:重写
    重写总结来说为:方法名相同,参数类型相同子类返回类型等于父类方法返回类型,子类抛出异常小于等于父类方法抛出异常,子类访问权限大于等于父类方法访问权限。详细的说明为:重写是子类对父类的允许访问的方法的实现过程进行重新编写,返回值和形参都不能改变。 即外壳不变,核心重写!重写的......
  • JavaSE基础05(方法,重载,调用,类和对象,构造器,封装,继承,方法重写,抽象类,接口,异常)
    面向对象以类的方式组织代码,以对象的组织封装数据;一个Java文件只能有一个public类,必须和文件名一样;java文件里也可以没有public类; 方法的定义方法的使用,修饰符返回值类型方法名(参数类型参数名){方法体return返回值};参数类型包括:基本数据类型和引用数据类......
  • 重写与重载
    关于Java中的重写与重载方法重载(Overload)方法重载是Java中一个很重要的概念,它是指在一个类中定义多个同名但参数列表不同的方法。是类名的不同表现形式。Java编译器会根据方法调用时传递的参数来选择调用哪个方法。方法重载的优势在于可以为相似功能的方法提供统一的方法名,方便......