重写equals方法 和 hashcode 方法 您所在的位置:网站首页 equals相等hashcode一定相等 重写equals方法 和 hashcode 方法

重写equals方法 和 hashcode 方法

2024-06-29 12:37| 来源: 网络整理| 查看: 265

问题: equals和hashCode有什么作用?他们是什么关系?

在java中,所有的对象都是继承于Object类,对象比较默认调用的是Object的equals方法 和 hashcode 方法 这两个方法是用来判断对象是否相等;

equals和hashCode源码:

public boolean equals(Object obj) { return (this == obj); } public native int hashCode();

equals: Object的equals方法的源码还是使用的==,比较两个对象的内存地址 1.对于值对象,==比较的是两个对象的值 2.对于引用对象,比较的是两个对象的地址 hashCode: Object的hashCode方法是基于对象的ID实现的,是根据内存地址换算出来的一个值,不同的对象可能存在相等的hashcode值; 它是一个native方法,可以根据平台自行实现,但它对其实现提出了一定的要求:

在同样的条件下,同一个对象无论运行多少次,返回的hash值必须一样。 如果两个对象通过equals方法比较判定相等,则他们通过hashCode方法返回的hash值也必须相等。 如果两个对象通过equals方法比较判定不相等,则不必保证他们通过hashCode返回的hash值不相等。

这两个方法的作用就是为了对象间的比较,而他们之间的关系都和其方法的规则和约束有关

对于没有重写equals和hashCode时的规定如下: 如果两个对象通过equals方法比较相等,则他们hashCode的返回值一定要相等。但如果两个对象的hashCode值相等,他们通过equals方法比较的返回值则不一定相等。 如果两个对象hashCode的返回值相等,不能判断两个对象是相等的。但如果两个对象的hashCode的返回值不相等,则可以判定两个对象一定不相等。

以上所说的规则可以简单归纳为:

两个对象相等,hashCode一定相等 两个对象不等,hashCode不一定不等 hashCode相等,两个对象不一定相等 hashCode不等,两个对象一定不等 为什么要重写equals方法 和 hashcode 方法

在实际的业务场景中,引用对象间的比较,引用对象的内容相同,我们就认为是同一个对象,而Object的equals方法 和 hashcode 方法是通过内存地址比较的,内容相同的对象地址值可能不一样,所以为了满足实际的业务场景,需要重写equals方法 和 hashcode 方法;

重写前的equals方法 和 hashcode 方法基于对象的ID实现,比较的是内存地址,两个内容相等的引用对象的内存地址可能不一样,所以需要重写equals方法和 hashcode方法,根据内容判断两个引用对象是否相同; 重写后的equals方法 和 hashcode 方法基于对象的内容实现,比较的是对象的内容; 为什么要同时重写equals方法和hashcode方法 这是一个通用约定,hashCode方法的常规约定,它声明相等的对象必须有相等的哈希码; 相同的对象的hashCode 的散列值最好保持相等, 而不同对象的散列值,我们也使其保持不相等。重写的equals方法保证对象的内容相同,内容相等的对象hashCode方法的散列值也必须相等,这才是对象相等的结果; 重写hashCode方法提升hashmap的性能:

重写了hashCode方法后,能够确保两个对象返回的的hashCode散列值是不一样的,这样一来, 在我们使用hashmap 去存储对象, 在进行验重逻辑的时候,咱们的性能就特别好了。 hashmap在插入值时对key(这里key是对象)的验重: HashMap中的比较key: 先求出key的hashcode(),比较其值是否相等; -若相等再比较equals(),若相等则认为他们是相等的。 -若equals()不相等则认为他们不相等。 如果只重写hashcode()不重写equals()方法,当比较equals()时只是看他们是否为同一对象(即进行内存地址的比较),所以必定要两个方法一起重写。

而HashSet, 用来判断key是否相等的方法,其实也是调用了HashMap加入元素方法,再判断是否相等。

使用list.contains()方法需重写equals():

它直接调用内部的indexof()方法去比较,可以看到内部是拿参数对象的equals()方法去比较,这个源生object对象的equals()方法, 可以看到比较的是两个对象的引用,重写equals()方法比较对象内容

场景:

我们现在需要比较两个对象 Pig 是否相等 。 而Pig 对象里面包含 三个字段, name,age,nickName ,我们现在只需要认为如果两个pig对象的name名字和age年龄一样,那么这两个pig对象就是一样的,nickName昵称不影响相等的比较判断。

public static void main(String[] args) { Pig pig1=new Pig(); pig1.setName("A"); pig1.setAge(11); pig1.setNickName("a"); String name= new String("A"); Pig pig2=new Pig(); pig2.setName(name); pig2.setAge(11); pig2.setNickName("B"); System.out.println(pig1==pig2); //false System.out.println(pig1.equals(pig2)); //false System.out.println(pig1.hashCode() ==pig2.hashCode()); //false }

pig1和pig2都是新new出来的,内存地址都是不一样的。

== : 比较内存地址 ,那肯定是false了 ; equals: 默认调用的是Object的equals方法,看下面源码图,显然还是使用了== ,那就还是比较内存地址,那肯定是false了; hashCode: 这是根据一定规则例如对象的存储地址,属性值等等映射出来的一个散列值,不同的对象存在可能相等的hashcode,但是概率非常小(两个对象equals返回true时,hashCode返回肯定是true;而两个对象hashCode返回true时,这两个对象的equals不一定返回true; 还有,如果两个对象的hashCode不一样,那么这两个对象一定不相等!)。 一个好的散列算法,我们肯定是尽可能让不同对象的hashcode也不同,相同的对象hashcode也相同。这也是为什么我们比较对象重写equals方法后还会一起重写hashcode方法。

接下来,重写equals方法和hashCode方法,实现我们这个Pig对象的比较,只要能保证name和age两个字段属性一致,就返回相等true。

重写equals方法: @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Pig pig = (Pig) o; boolean nameCheck=false; boolean ageCheck=false; if (this.name == pig.name) { nameCheck = true; } else if (this.name != null && this.name.equals(pig.name)) { nameCheck = true; } if (this.age == pig.age) { ageCheck = true; } else if (this.age != null && this.age.equals(pig.age)) { ageCheck = true; } if (nameCheck && ageCheck){ return true; } return false; } 重写hashCode方法:

重写hashcode方法的时候,为什么要写个31啊? 之所以使用 31, 是因为他是一个奇素数。 如果乘数是偶数,并且乘法溢出的话,信息就会丢失,因为与2相乘等价于移位运算(低位补0)。 使用素数的好处并不很明显,但是习惯上使用素数来计算散列结果。 31 有个很好的性能,即用移位和减法来代替乘法, 可以得到更好的性能: 31 * i == (i



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有