基本概念
hashCode()
方法作用:JDK8的原生注释如下:
- 简单翻译就是:返回这个对象的hash code
- 什么是hash code后文也会展开解释
/**
* Returns a hash code value for the object.
*/
Java基类Object的代码实现如下(是一个本地方法):
public native int hashCode();
equals()
方法作用:JDK8的原生注释如下:
- 简单翻译就是:表明其他对象是否“equal to”当前对象
- 如何实现“equal to”,后文会展开解释
/**
* Indicates whether some other object is "equal to" this one.
*/
Java基类Object的代码实现如下:
public boolean equals(Object obj) {
return (this == obj);
}
常见面试题
==和equals()有什么区别
-
==
-
对于基本数据类型:==就是比较值是否相等
-
对于引用数据类型:==就是比较引用的内存地址
-
demo代码如下:
- 虽然s1和i1的数据类型不同,但是由于值相等,所以返回true
- integer1和integer2是两个不同的对象,引用指向的内存地址也不同,所以返回false
public static void main(String[] args) { short s1 = 1; int i1 = 1; System.out.println(s1 == i1); // 返回true Integer integer1 = new Integer(1); Integer integer2 = new Integer(2); System.out.println(integer1 == integer2); //返回false }
-
-
equals()
- Object的equals()方法实现等同于==.(可以参照基本概念章节中的equals()源码)
- 实际应用中一般会重写equals()方法,用于比较对象是否相等
两个对象的 hashCode() 相同,则 equals() 也一定为 true,对吗?
我在网上看了很多帖子都说这句话是错误的,严格意义来说这个题目不够严谨,这也是我写这篇帖子的初衷.这个题目的关键在于对象是否重写hashCode()方法.
-
对象没有重写hashCode()方法
- Object的hashCode()方法实现是返回对象的内存地址(可以通过JVM参数-XX:hashCode指定hashCode的生成策略)
- 返回题目,hashCode相同,也就是内存地址相同,也就是同一个对象
- 既然是同一个对象equals()返回肯定为true
-
对象重写hashCode()方法
- 两个对象hashCode()相同,equals()也一定为true是错误的
- hashCode是基于hash算法计算的,不同的对象计算的hashCode结果可能相同
- 网上大多数帖子都是用下面例子,证明这个题目错误的
- 这个代码的关键也是因为String重写了hashCode()方法导致的
public static void main(String[] args) { String s1 = "通话"; String s2 = "重地"; System.out.println(s1.hashCode() == s2.hashCode()); //true System.out.println(s2.equals(s1)); //false }
两个对象equals()返回为true,那么他们的hashCode()一定相同(),对吗?
严格意义来说这句话是不对的,证明代码如下:
- 重写了equals()方法,无论怎么比较都是返回true
- 为了预防一些“杠精”,这个代码可以把equals修改为比较对象的name属性
- 没有重写hashCode方法,也就是复用Object的hashCode方法
- 这个代码的执行结果equals一定为true,但是hashCode方法不相同
public class TestUserEquals {
private String name;
private String age;
public static void main(String[] args) {
TestUserEquals u1 = new TestUserEquals("cjq", "1");
TestUserEquals u2 = new TestUserEquals("lsy", "2");
System.out.println(u1.equals(u2)); // true
System.out.println(u1.hashCode() == u2.hashCode()); //false
}
public boolean equals(Object obj) {
return true;
}
}
既然这句话不对,为什么网上很多人都认为这句话是正确的呢?
- 这源于Object的hashCode源码的注释,源码注释如下:
- 大概意思就是:如果两个对象equals返回为true,调用hashCode方法必须产生相同的整数结果
/*<li>If two objects are equal according to the {@code equals(Object)}
* method, then calling the {@code hashCode} method on each of
* the two objects must produce the same integer result.
*/
为什么重写了equals()方法,就要重写hashCode()方法
-
基于hash算法的容器增加元素的核心步骤
-
从上图可以看到基于hash算法的容器,在添加元素的时候会先比较元素的hashCode,假设现在我们有一个对象User(对象有两个属性name和age),我们重写了equals方法,认为name相同的对象equals返回就是true,但是我们没有重写hashCode方法(也就是Object的hashCode方法,返回对象的内存地址),那么就算name相同的对象的hashCode也永远不会相同,就会被重复添加进入容器.也就违背了我们按照name相同去重的初衷
-
为什么有hashCode分组概念?为了提高性能
- 如果目前容器已经添加了1000个元素,且没有hashCode分组概念,现在需要添加元素
- 那么这个元素就需要和1000个元素进行equals比较,也就是循环1000次
- 如果有了hashCode分组的概念,hash算法有比较优质,把1000个元素分成了10组,每个元素100个元素
- 此时添加元素时,如果hash冲突也就比较100次
- 如果没有hash冲突,直接添加进入容器
总结
- 大家准备面试过程中不要死记硬背,抓住题目关键,讲清楚原理相信也可以打动面试官
- 网上帖子的结论大家也要有自己的判断,最好可以动手验证