先以一段代码开始这篇blog。
03 |
private String first; //first name |
04 |
private String last; //last name |
06 |
public String getFirst() { |
10 |
public void setFirst(String first) { |
14 |
public String getLast() { |
18 |
public void setLast(String last) { |
22 |
public Name(String first, String last) { |
28 |
public boolean equals(Object object) { |
29 |
Name name = (Name) object; |
31 |
return first.equals(name.getFirst()) && last.equals(name.getLast()); |
34 |
public static void main(String[] args) { |
35 |
Map<Name, String> map = new HashMap<Name, String>(); |
36 |
map.put(new Name("mali", "sb"), "yes"); |
38 |
System.out.println("is the key existed? ture or false? -> " |
39 |
+ map.containsKey(new Name("mali", "sb"))); |
那输出结果是什么呢?类似这样的题目总能遇到,以前不知道有什么好考的,弱智?自己动手尝试了一次,发现结果不是自己想象的那样。本篇就用来揭示HashMap的equals与hashCode中你不知道的秘密。结果如下:
is the key existed? ture or false? -> false
对的,结果就是false,我很不理解为什么这样,已经重写了equals函数了啊!当时真心不服气,就在equals函数里面打了断点,然后更让我难以置信的事情发生了,断点处没有停。非常困惑,不过还好,jdk的源码在手上,去查了HashMap中containsKey函数的源码。源码结构如下图:

从图中可以看出,真正干活的是getEntry(Object key),重点看如下两行:
2 |
((k = e.key) == key || (key != null && key.equals(k)))) |
从if条件上看,是一个短路与,首先要判断两个对象的hash值是否相等。如果相等才进行后续的判断。或者换一个说法,在HashMap中只有两个对象的hash值相等的前提下才会执行equals方法的逻辑。关于这一点,有两个佐证。
自己编程验证。
在文章开头的基础上,做了点儿改进,输出两个对象的hash值,并且在equals方法中打印一行文字。如下:
03 |
private String first; //first name |
04 |
private String last; //last name |
06 |
public String getFirst() { |
10 |
public void setFirst(String first) { |
14 |
public String getLast() { |
18 |
public void setLast(String last) { |
22 |
public Name(String first, String last) { |
28 |
public boolean equals(Object object) { |
29 |
System.out.println("equals is running..."); |
30 |
Name name = (Name) object; |
32 |
return first.equals(name.getFirst()) && last.equals(name.getLast()); |
35 |
public static void main(String[] args) { |
36 |
Map<Name, String> map = new HashMap<Name, String>(); |
37 |
Name n1 = new Name("mali", "sb"); |
38 |
System.out.println("the hashCode of n1 : " + n1.hashCode()); |
40 |
Name n2 = new Name("mali", "sb"); |
41 |
System.out.println("the hashCode of n2 : " + n2.hashCode()); |
42 |
System.out.println("is the key existed? ture or false? -> " |
43 |
+ map.containsKey(n2)); |
结果:
the hashCode of n1 : 1690552137
the hashCode of n2 : 1901116749
is the key existed? ture or false? -> false
从执行结果可以看出1、两个对象的hash值是不相同的;2、equals方法确实也没有执行。
再次对代码进行改进,加入重写的hashCode方法,如下,看看这次的结果会是怎样。
03 |
private String first; //first name |
04 |
private String last; //last name |
06 |
public String getFirst() { |
10 |
public void setFirst(String first) { |
14 |
public String getLast() { |
18 |
public void setLast(String last) { |
22 |
public Name(String first, String last) { |
28 |
public boolean equals(Object object) { |
29 |
System.out.println("equals is running..."); |
30 |
Name name = (Name) object; |
32 |
return first.equals(name.getFirst()) && last.equals(name.getLast()); |
35 |
public int hashCode() { |
36 |
System.out.println("hashCode is running..."); |
37 |
return first.hashCode() + last.hashCode(); |
40 |
public static void main(String[] args) { |
41 |
Map<Name, String> map = new HashMap<Name, String>(); |
42 |
Name n1 = new Name("mali", "sb"); |
43 |
System.out.println("the hashCode of n1 : " + n1.hashCode()); |
45 |
Name n2 = new Name("mali", "sb"); |
46 |
System.out.println("the hashCode of n2 : " + n2.hashCode()); |
47 |
System.out.println("is the key existed? ture or false? -> " |
48 |
+ map.containsKey(n2)); |
结果:
hashCode is running...
the hashCode of n1 : 3347552
hashCode is running...
hashCode is running...
the hashCode of n2 : 3347552
hashCode is running...
equals is running...
is the key existed? ture or false? -> true
同样从结果中可以看出:在hash值相等的情况下,equals方法也执行了,HashMap的containsKey方法也像预想的那样起作用了。
结论:
在使用HashSet(contains也是调用HashMap中的方法)、HashMap等集合时,如果用到contains系列方法时,记得需同时重写equals与hashCode方法。
- Java中equals,hashcode
在Java语言中,Object对象中包含一个equals和hashCode方法,其中hashCode方法是由JVM本地代码(native code)实现的,返回值是一个有符号的32位整数,对 ...
- [转载] HashMap的工作原理-hashcode和equals的区别
目录 前言 为什么需要使用Hashcode,可以从Java集合的常用需求来描述: 更深入的介绍 先来些简单的问题 HashMap的0.75负载因子 总结 我在网上看到的这篇文章,介绍的很不错,但是我看 ...
- == 和 equals,equals 与 hashcode,HashSet 和 HashMap,HashMap 和 Hashtable
一:== 和 equals == 比较引用的地址equals 比较引用的内容 (Object 类本身除外) String obj1 = new String("xyz"); Str ...
- 堆、栈、内存分配、==、equals、hashcode详解(转载)
问题的引入: 问题一:String str1 = "abc";String str2 = "abc";System.out.println(str1==str2 ...
- 如何在Java中避免equals方法的隐藏陷阱
摘要 本文描述重载equals方法的技术,这种技术即使是具现类的子类增加了字段也能保证equal语义的正确性. 在<Effective Java>的第8项中,Josh Bloch描述了当继 ...
- Java == ,equals 和 hashcode 的区别和联系(阿里面试)
今天阿里的人问我 equals 与hashcode的区别,我答不上来, 仔细查了一下,做了总结: (1) == 这是Java 比较内存地址,就是内存中的对象: java中的==是比较两个对象在JVM中 ...
- Object之equals和hashCode
译者注 :你可能会觉得Java很简单,Object的equals实现也会非常简单,但是事实并不是你想象的这样,耐心的读完本文,你会发现你对Java了解的是如此的少.如果这篇文章是一份Java程序员的入 ...
- 关于重写equals()和hashCode()的思考
最近这几天一直对equals()和hashCode()的事搞不清楚,云里雾里的. 为什么重写equals(),我知道. 但是为什么要两个都要重写呢,我就有点迷糊了,所以趁现在思考清楚后记录一下. 起因 ...
- 如何编写出高质量的 equals 和 hashcode 方法?
什么是 equals 和 hashcode 方法? 这要从 Object 类开始说起,我们知道 Object 类是 Java 的超类,每个类都直接或者间接的继承了 Object 类,在 Object ...
随机推荐
- HDU 1847 Good Luck in CET-4 Everybody!(SG函数)
Good Luck in CET-4 Everybody! Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K ...
- Go语言之并发编程(四)
同步 Go 程序可以使用通道进行多个 goroutine 间的数据交换,但这仅仅是数据同步中的一种方法.通道内部的实现依然使用了各种锁,因此优雅代码的代价是性能.在某些轻量级的场合,原子访问(atom ...
- XX公司在线笔试题编程题之一
题目: #include <iostream> #include <vector> #include <string> #include <list> ...
- Eclipse 读取config目录下文件
最近在一个项目,在项目下新建了一个config配置文件夹,添加一个配置文件config.properties. 使用classpath:config.properties方式加载配置文件, 具体实现代 ...
- Java并发之(3):锁
锁是并发编程中的重要概念,用来控制多个线程对同一资源的并发访问,在支持并发的编程语言中都有体现,比如c++ python等.本文主要讲解Java中的锁,或者说是重入锁.之所以这么说是因为在Java中, ...
- 虚拟机安装教程(linux、centOS)
前提: Windows7系统 64位 VMware-workstation-full-11.0.0-2305329.exe CentOS-6.6-x86_64-bin-DVD1.iso 安装步骤: 看 ...
- mate viewport
<meta name="viewport" content="width=device-width,height=device-height,initial-sca ...
- [python][django学习篇][12]继续设计博客首页,点击博客标题能显示文章的详情
回顾一下开发流程:配置url, 编写视图函数,编写对应模板 配置URL 首页视图匹配的 URL 去掉域名后,是一个空的字符串.每篇文章的详情有着不同的 URL,因此可以设计文章详情页面URl:< ...
- [错误解决]ubuntu 不在 sudoers 文件中。此事将被报告。
跟据报错判断,ubuntu没有sudo权限,经过查询需要将ubuntu账户加入/etc/sudoers中 先切换到root权限 su 输入密码 修改/etc/sudoers配置 vim /etc/su ...
- excel模板解析前后设计变化,以及我对此的看法和感受
前言:近期也在做Excel模板的解析工作,目前来说,应该是第三版了.我最开始做的,就是垒鸡窝,虽然考虑了1.0提出的关于excel解析的一些建议和问题(单个模板),但是真的毫无设计可言.就几个工具类, ...