「每日一面」答疑解惑 "=" 、equals 与 hashCode 相关面试题

Q: “==”比较的是什么

  • ==是一个运算符
  • 对于8种基本的数据类型,比较的是变量所对应内存存储的数值
  • 对于指向对象的变量,比较的也是变量所对应内存存储的数值(即指向的对象占用堆内存的首地址),也就是比较变量是否指向同一个对象。
「每日一面」答疑解惑

输出结果:

a==b:truea==c:falses1==s2:trues1==s3:false

上述代码中:

如果一个变量指向的数据是对象类型的,那么就涉及两块内存,变量占用的栈内存和对象占用的堆内存。例如Object obj = new Object();变量obj是存储在栈内存,new Object()存储在堆内存,此时,变量obj所对应的内存中存储的数值就是对象占用的那块内存的首地址。

Q: “equals”比较的是什么

  • equals是根类Object中的方法
  • 源代码如下:
public boolean equals(Object obj) { return (this == obj); }
  • equals方法是用于比较两个独立对象的内容是否相同,需要为类重写equals方法。

例如,对于下面的代码:

String a=new String("foo");String b=new String("foo");

两条new语句创建了两个对象,然后用a与b这两个变量分别指向了其中一个对象,这是两个不同的对象,它们的首地址是不同的,即a和b中存储的数值是不相同的,所以,表达式a==b将返回false,而String类重写了equals方法,两个变量中的内容是相同的。所以,表达式a.equals(b)将返回true。

在实际开发中,我们经常要比较传递进行来的字符串内容是否等,

例如,

String input = "xxxxx";input.equals("quit");

许多人稍不注意就使用==进行比较了,记住,字符串的比较基本上都是使用equals方法。

如果一个类没有自己定义equals方法,那么它将继承Object类的equals方法,Object类的equals方法的实现代码如下:

boolean equals(Object o){ return this==o;}

总结:

如果一个类没有定义自己的的equals方法,它默认的equals方法就是从Object继承来的equals方法,就相当于“==”操作符。也是在比较两个变量指向的对象是否是同一对象,这时候使用equals和使用==会得到同样的结果,如果你编写的类希望能够比较该类创建的两个实例对象的内容是否相同,那么你必须覆盖equals方法,由你自己写代码来决定在什么情况即可认为两个对象的内容是相同的。

String类中重写的equals方法源码:

「每日一面」答疑解惑

可以看出

String类中重写的equals()是按照下面顺序判断的:

1.先判断地址是否相同,若想等执行2,否则返回false,

2.然后判断是否是String类型,否则返回false,

3.然后依次判断每一个字符是否相等,全部相等返回true,否则返回false,

Q:Java 中 hashCode() 的作用是什么?

答:hashCode() 的作用是为了提高在散列结构存储中查找的效率,在线性表中没有作用;只有每个对象的 hash 码尽可能不同才能保证散列的存取性能,事实上 Object 类提供的默认实现确实保证每个对象的 hash 码不同(在对象的内存地址基础上经过特定算法返回一个 hash 码)。在 Java 有些集合类(HashSet)中要想保证元素不重复可以在每增加一个元素就通过对象的 equals 方法比较一次,那么当元素很多时后添加到集合中的元素比较的次数就非常多了,也就是说如果集合中现在已经有 3000 个元素则第 3001 个元素加入集合时就要调用 3000 次 equals 方法,这显然会大大降低效率,于是 Java 采用了哈希表的原理,这样当集合要添加新的元素时会先调用这个元素的 hashCode 方法就一下子能定位到它应该放置的物理位置上(实际可能并不是),如果这个位置上没有元素则它就可以直接存储在这个位置上而不用再进行任何比较了,如果这个位置上已经有元素了则就调用它的 equals 方法与新元素进行比较,相同的话就不存,不相同就散列其它的地址,这样一来实际调用 equals 方法的次数就大大降低了,几乎只需要一两次,而 hashCode 的值对于每个对象实例来说是一个固定值。


Q:两个对象值相同 (tmp1.equals(tmp2) == true) 但却可有不同的 HashCode 值,这句话有问题吗?

答:有问题,这句话是不对的。两个对象 tmp1 和 tmp2 满足 tmp1.equals(tmp2) == true 时它们的 HashCode 应当相同,因为 Java 对于 eqauls 方法和 hashCode 方法的规定是如果两个对象 equals 方法相等则它们的 hashCode 值一定要相同,如果两个对象的 hashCode 相同则它们的 equals 方法并不一定相同;实际中我们也可以不按照要求的原则去做,但是如果违背了上述原则就会发现在使用容器时相同的对象可以出现在 Set 集合中,同时增加新元素的效率会大大下降(对于使用哈希存储的系统,如果哈希码频繁的冲突将会造成存取性能急剧下降)。

Q:可以直接根据 hashCode() 方法产生的值判断两个对象是否相等吗?

答:不能。因为 hashCode()方法的由来是根据这个对象内存储的数据及对象的一些特征来做散列并返回一个有符号的 32 位哈希值,所以 hashCode() 方法返回的是一个散列值,而对于一个散列来说不同的内容也是可能会出现相同的散列值,所以即使两个对象的 hashCode() 返回值一样也并不能代表两个对象是相等的,要判断两个对象是否相等还是需要使用 equals() 方法。不过要注意:两个对象的 hashCode() 返回值相等不能判断这两个对象是相等的,但是两个对象的 hashCode() 返回值不相等则可以判断这两个对象一定不相等(原因如上题指导原则)。


Q:说说 hashCode() 的返回值和 == 的关系?

答:若 == 返回 true 则两边对象的 hashCode() 返回值必须相等,若 == 返回 false 则两边对象的 hashCode() 返回值有可能相等,也有可能不等;因为在 Java 中对象默认的 equals 方法实现就是 == 比较,而 Java 对于 eqauls 方法和 hashCode 方法的规定是如果两个对象 equals 方法相等则它们的 hashCode 值一定要相同,如果两个对象的 hashCode 相同则它们的 equals 方法并不一定相同,所以可得出上面结论。

Q:Java 中为什么重写 equals(str) 方法时尽量要重写 hashCode() 方法?

答:这是一个检测实际项目踩坑经验的题目,因为我们经常会犯的一个很常见而又低级的错误根源在于重写 equals 方法时没有重写 hashCode 方法。自定义类重写 equals 方法是用来进行等值比较,重写 compareTo 方法是用来进行不同对象大小比较,而重写 hashCode 方法是为了将数据存入 HashSet、HashMap、Hashtable 等基于哈西表的集合类时进行高效比较。

hashCode 方法的常规约定如下:

  • 程序执行期间只要对象 equals 方法比较操作所用到的信息没有被修改,则对这同一个对象无论调用多次 hashCode 方法都必须返回同一个整数。
  • 如果两个对象根据 equals 方法比较是相等的则调用这两个对象中任意一个对象的 hashCode 方法都必须产生同样的整数结果。
  • 如果两个对象根据 equals 方法比较是不相等的,则调用这两个对象中任意一个对象的 hashCode 方法不一定要产生相同的整数结果(尽量保证不相等的对象产生截然不同的整数结果是可以提高散列表性能的)。

Q:请补全下面代码空缺(即实现其 hashCode 方法)?

「每日一面」答疑解惑

答:此题就是在考察如何在重写 equals() 方法时正确的重写 hashCode() 方法。其实重写 hashcode() 方法有如下几个原则可以遵循:

  • 如果重写了 equals() 方法,且 equals() 方法判断相等则 hashCode() 方法也要保证必须相等。
  • 重写 hashCode() 方法算法也不能太过简单,否则哈希冲突过多。
  • 重写 hashCode() 方法算法也不能太过复杂,否则计算复杂度过高而影响性能。

《Effective Java》书中给出的一种算法,基于 17 和 31 散列码思想的实现,如下:

「每日一面」答疑解惑

当然,这道题如果不限制 equals 方法的实现则完全可以用 JDK7 开始提供的 java.util.Objects 来重写 equals 和 hashCode 方法,代码如下:

「每日一面」答疑解惑

使用上面这种 Objects 方式重写 equals 和 hashCode 可能是最简单和最优雅的实现了,所以我们项目中推荐采纳。 参考文章:https://www.jianshu.com/p/a16c856b8f57,码农每日一题

关于 HashCode 与 equals 的更多详情可以参考下文:

《equals 和 HashCode 深入理解以及 Hash 算法原理》

http://blog.csdn.net/qq_21688757/article/details/53067814

《浅谈 Java 中的 hashcode 方法》

http://www.cnblogs.com/dolphin0520/p/3681042.html

「每日一面」答疑解惑


分享到:


相關文章: