HashSet和HashMap一直都是JDK中最常用的两个类,HashSet要求不能存储相同的对象,HashMap要求不能存储相同的键。 
那么Java运行时环境是如何判断HashSet中相同对象、HashMap中相同键的呢?当存储了“相同的东西”之后Java运行时环境又将如何来维护呢?

在研究这个问题之前,首先说明一下JDK对equals(Object obj)和hashcode()这两个方法的定义和规范: 
在Java中任何一个对象都具备equals(Object obj)和hashcode()这两个方法,因为他们是在Object类中定义的。 
equals(Object obj)方法用来判断两个对象是否“相同”,如果“相同”则返回true,否则返回false。 
hashcode()方法返回一个int数,在Object类中的默认实现是“将该对象的内部地址转换成一个整数返回”。 
接下来有两个个关于这两个方法的重要规范(我只是抽取了最重要的两个,其实不止两个): 
规范1:若重写equals(Object obj)方法,有必要重写hashcode()方法,确保通过equals(Object obj)方法判断结果为true的两个对象具备相等的hashcode()返回值。说得简单点就是:“如果两个对象相同,那么他们的hashcode应该 相等”。不过请注意:这个只是规范,如果你非要写一个类让equals(Object obj)返回true而hashcode()返回两个不相等的值,编译和运行都是不会报错的。不过这样违反了Java规范,程序也就埋下了BUG。 
规范2:如果equals(Object obj)返回false,即两个对象“不相同”,并不要求对这两个对象调用hashcode()方法得到两个不相同的数。说的简单点就是:“如果两个对象不相同,他们的hashcode可能相同”。 
根据这两个规范,可以得到如下推论: 
1、如果两个对象equals,Java运行时环境会认为他们的hashcode一定相等。 
2、如果两个对象不equals,他们的hashcode有可能相等。 
3、如果两个对象hashcode相等,他们不一定equals。 
4、如果两个对象hashcode不相等,他们一定不equals。

这样我们就可以推断Java运行时环境是怎样判断HashSet和HastMap中的两个对象相同或不同了。我的推断是:先判断hashcode是否相等,再判断是否equals。

测试程序如下:首先我们定义一个类,重写hashCode()和equals(Object obj)方法

然后写一个测试类,代码如下:

运行之后打印结果是:

判断hashcode 
判断hashcode 
判断equals 
2

可以看出,Java运行时环境会调用new A()这个对象的hashcode()方法。其中: 
打印出的第一行“判断hashcode”是第一次map.put(new A(), new Object())所打印出的。 
接下来的“判断hashcode”和“判断equals”是第二次map.put(new A(), new Object())所打印出来的。

那么为什么会是这样一个打印结果呢?我是这样分析的: 
1、当第一次map.put(new A(), new Object())的时候,Java运行时环境就会判断这个map里面有没有和现在添加的 new A()对象相同的键,判断方法:调用new A()对象的hashcode()方法,判断map中当前是不是存在和new A()对象相同的HashCode。显然,这时候没有相同的,因为这个map中都还没有东西。所以这时候hashcode不相等,则没有

  1. import java.util.HashMap;
  2. import java.util.Map;
  3. class A {
  4. @Override
  5. public boolean equals(Object obj) {
  6. System.out.println("判断equals");
  7. return true;
  8. }
  9. @Override
  10. public int hashCode() {
  11. System.out.println("判断hashcode");
  12. return 1;
  13. }
  14. }
  15. public class Test {
  16. public static void main(String[] args) {
  17. Map<A,Object> map = new HashMap<A, Object>();
  18. map.put(new A(), new Object());
  19. map.put(new A(), new Object());
  20. System.out.println(map.size());
  21. }
  22. }
  1. import java.util.HashMap;
  2. import java.util.Map;
  3. class A {
  4. @Override
  5. public boolean equals(Object obj) {
  6. System.out.println("判断equals");
  7. return true;
  8. }
  9. @Override
  10. public int hashCode() {
  11. System.out.println("判断hashcode");
  12. return 1;
  13. }
  14. }
  15. public class Test {
  16. public static void main(String[] args) {
  17. Map<A,Object> map = new HashMap<A, Object>();
  18. map.put(new A(), new Object());
  19. map.put(new A(), new Object());
  20. System.out.println(map.size());
  21. }
  22. }

必要再调用 equals(Object obj)方法了。参见推论4(如果两个对象hashcode不相等,他们一定不equals) 
2、当第二次map.put(new A(), new Object())的时候,Java运行时环境再次判断,这时候发现了map中有两个相同的hashcode(因为我重写了A类的hashcode()方 法永远都返回1),所以有必要调用equals(Object obj)方法进行判断了。参见推论3(如果两个对象hashcode相等,他们不一定equals),然后发现两个对象不equals(因为我重写了equals(Object obj)方法,永远都返回false)。 
3、这时候判断结束,判断结果:两次存入的对象不是相同的对象。所以最后打印map的长度的时候显示结果是:2。

改写程序如下:

运行之后打印结果是:

判断hashcode 
判断hashcode 
判断equals 
1

显然这时候map的长度已经变成1了,因为Java运行时环境认为存入了两个相同的对象。原因可根据上述分析方式进行分析。

以上分析的是HashMap,其实HashSet的底层本身就是通过HashMap来实现的,所以他的判断原理和HashMap是一样的,也是先判断hashcode再判断equals。

所以:写程序的时候应尽可能的按规范来,不然在不知不觉中就埋下了bug!

hashCode() 和equals() 区别和作用的更多相关文章

  1. hashCode() 和equals() 区别和作用(转)

    出处:https://www.jianshu.com/p/5a7f5f786b75 本章的内容主要解决下面几个问题: 1 equals() 的作用是什么? 2 equals() 与 == 的区别是什么 ...

  2. hashcode和equals区别

    hashcode:对象的初始地址的整数表示 Java中的对象是JVM在管理,JVM会在她认为合适的时候对对象进行移动,比如,在某些需要整理内存碎片的GC算法下发生的GC.此时,对象的地址会变动,但ha ...

  3. hashCode()与equals()区别

    这两个方法均是超类Object自带的成员方法.Object类是所有Java类的祖先.每个类都使用 Object 作为超类.所有对象(包括数组)都实现这个类的方法.在不明确给出超类的情况下,Java会自 ...

  4. hashCode 和 equals 的区别

    今天记录一下hashCode的知识,以前都没有怎么接触过的,感觉还是很陌生,专门去学习了一下 首先我最大的问题就是hashCode究竟是干什么 的,现在也一知半解了吧, 哈希值是一个对象的地址值,是一 ...

  5. K:java中的hashCode和equals方法

      hashCode和equals方法是Object类的相关方法,而所有的类都是直接或间接的继承于Object类而存在的,为此,所有的类中都存在着hashCode和equals.通过翻看Object类 ...

  6. (转)从一道面试题彻底搞懂hashCode与equals的作用与区别及应当注意的细节

    背景:学习java的基础知识,每次回顾,总会有不同的认识.该文系转载 最近去面试了几家公司,被问到hashCode的作用,虽然回答出来了,但是自己还是对hashCode和equals的作用一知半解的, ...

  7. hashCode与equals的作用与区别及应当注意的细节

    最近去面试了几家公司,被问到hashCode的作用,虽然回答出来了,但是自己还是对hashCode和equals的作用一知半解的,所以决定把它们研究一下. 以前写程序一直没有注意hashCode的作用 ...

  8. 彻底搞懂hashCode与equals的作用与区别及应当注意的细节

    以前写程序一直没有注意hashCode的作用,一般都是覆盖了equals,缺没有覆盖hashCode,现在发现这是埋下了很多潜在的Bug!今天就来说一说hashCode和equals的作用. 先来试想 ...

  9. java中hashcode和equals的区别和联系

    HashSet和HashMap一直都是JDK中最常用的两个类,HashSet要求不能存储相同的对象,HashMap要求不能存储相同的键. 那么Java运行时环境是如何判断HashSet中相同对象.Ha ...

随机推荐

  1. [转] webpack debug in webstorm

    先run build,然后用node server.js来做 WebStorm 11 adds support for debugging client-side apps built with We ...

  2. Java基础知识强化之集合框架笔记43:Set集合之TreeSet存储Integer类型的元素并遍历

    1. TreeSet类概述: • 能够对元素按照某种规则进行排序. • 或者根据创建set时提供的Comparator进行排序 • 具体取决于使用的构造方法 2. 代码示例: package cn.i ...

  3. 零基础学习云计算及大数据DBA集群架构师【预科2015年12月14日周一】

    1.第一天比较轻松,上午填表格,录指纹,拍照片,做自我介绍. 2.下午老师简单介绍了一下PC\交换机\路由器\塔式服务器\机架式服务器(1U\2U)\刀片服务器\磁带机 3.班主任陈老师朱老师,预科秦 ...

  4. HTML5移动开发中的input输入框类型

    HTML5规范引入了许多新的input输入框类型 在HTML5移动开发中,通过这些新的输入框类型来显示定制后的键盘布局,用户体验更好,更容易填写各种表单 本文中,实测手机为肾4S与米4 数字类型num ...

  5. HTML5 文件域+FileReader 读取文件(二)

    一.读取文本文件内容,指定字符编码 <div class="container"> <!--文本文件验证--> <input type="f ...

  6. Angularjs总结(六) 上传附件

    所用插件:angular-file-upload 这个插件用到的几个指令:nv-file-select(点击选择).uploader(用于绑定控制器中新建的uploader对象) HTML: < ...

  7. 转: Oracle AWR 报告 每天自动生成并发送邮箱

    原贴地址:http://www.cnblogs.com/vigarbuaa/archive/2012/09/05/2671794.html Oracle AWR 介绍http://blog.csdn. ...

  8. iOS菜鸟之FMDB的二次封装简单易用

    闲来无事写点东西,希望大家多多指正! 大家先去git下载FMDB,然后将其中source文件夹中的fmdb文件夹拖入自己的项目中.最后就可以引用下面的代码对fmdb进行一次简单的封装. 这样可以更直观 ...

  9. iOS 去除导航栏下的黑线

    Swift: navigationController?.navigationBar.setBackgroundImage(UIImage(), forBarMetrics: .Default) na ...

  10. Swift中可选类型(Optional)的用法 以及? 和 ! 的区别 (转载博客,知识分享)

    本文转载自:代码手工艺人的博客,原文名称:Swift之 ? 和 ! Swift语言使用var定义变量,但和别的语言不同,Swift里不会自动给变量赋初始值,也就是说变量不会有默认值,所以要求使用变量之 ...