equals()和hashCode()都是Object类中的方法,而Object类又作为Java中的顶级父类,这使绝大多数的类也都继承到了这两个方法
equals方法用于比较两个对象是否相同,Object类中equals方法的实现是比较引用地址来判断的对象是否是同一个对象,通过覆盖该方法可以实现自定义的判断规则;
object的eqauls()方法:
public boolean equals(Object obj) {
return (this == obj);
}
hashCode是jdk根据对象的地址或者字符串或者数字计算该对象的哈希码值的方法。
当然我们也可以重写这两个方法
String中的equals()方法:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
return (anObject instanceof String aString)
&& (!COMPACT_STRINGS || this.coder == aString.coder)
&& StringLatin1.equals(value, aString.value);
}
Stringequals()方法比较的是字符串的内容
为什么重写equals()
1.Object类中equals方法比较的是两个对象的引用地址,只有对象的引用地址指向同一个地址时,才认为这两个地址是相等的,否则这两个对象就不想等。
2.如果有两个对象,他们的属性是相同的,但是地址不同,这样使用equals()比较得出的结果是不相等的,而我们需要的是这两个对象相等,因此默认的equals()方法是不符合我们的要求的,这个时候我们就需要对equals()方法进行重写以满足我们的预期结果。
3.在java的集合框架中需要用到equals()方法进行查找对象,如果集合中存放的是自定义类型,并且没有重写equals()方法,则会调用Object父类中的equals()方法按照地址比较,往往会出现错误的结果,此时我们应该根据业务需求重写equals()方法。
String中的hashCode()方法:
public int hashCode() {
// The hash or hashIsZero fields are subject to a benign data race,
// making it crucial to ensure that any observable result of the
// calculation in this method stays correct under any possible read of
// these fields. Necessary restrictions to allow this to be correct
// without explicit memory fences or similar concurrency primitives is
// that we can ever only write to one of these two fields for a given
// String instance, and that the computation is idempotent and derived
// from immutable state
int h = hash;
if (h == 0 && !hashIsZero) {
h = isLatin1() ? StringLatin1.hashCode(value)
: StringUTF16.hashCode(value);
if (h == 0) {
hashIsZero = true;
} else {
hash = h;
}
}
return h;
}
String的hashCode()方法根据字符串的内容生成一个hash值
为什么要重写hashCode()
1.hashCode()方法用于散列数据的快速存储,HashSet/HashMap/Hashtable类存储数据时都是根据存储对象的hashcode值来进行分类存储的,一般先根据hashcode值在集合中进行分类,在根据equals()方法判断对象是否相同。
2.HashMap对象是根据其Key的hashCode来获取对应的Value。
3.生成一个好的hashCode值能提高HashSet查找的性能,差的hashCode值不但不能提高性能,甚至可能造成错误。比如hashCode方法中返回常量,会让,HashSet的查找效率退化为List集合的查找效率;hashCode方法中返回随机数,会让查找结果变的不可预测。
4.好的hashCode生成方式是让对象中的关键属性与质数相乘,并将积相加获取。
介绍完了equals()和hashCode(),4,就来说说为什么要同时重写它们两个
hashset和hashmap的底层是通过数组+链表来存储的
存储一个元素时先调用它的hashCode()得到一个hash值,再通过这个hash确定一个nade[]数组的一个索引,然后调用equals()和该索引处链表中的元素比较如果都不同,再添加到链表上。
以String为例
如果我们只重写hashCode()
String str1="hello";String str2="hello"
str1.hashCode()和str2.hashCode()虽然是相同的,但之后str1.equals(str2)的结果却是false,因为不重写equals(),str1.equals()调用的是object的equals()方法,比较的是str1和str2的地址,这时集合中可能就会出现两个内容为hello的字符串元素。
如果我们只重写equals()
String str1="hello";String str2="hello"
str1.equals(str2)虽然为true,但str1.hashCode()和str2.hashCode()有可能是不相同的(当地址计算出的hash发生hash冲突是,即使地址不同通过地址得到的hash值可能相同),这时集合中也可能会出现两个内容为hello的字符串元素。
所以在使用集合存储我们自定义引用类型时,我们必须同时重写这两个方法