`

如果两个对象不相同,他们的hashcode可能相同?

    博客分类:
  • Java
阅读更多

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)方法 

class A { 
 
    @Override 
    public boolean equals(Object obj) { 
        System.out.println("判断equals"); 
        return false; 
    } 
 
    @Override 
    public int hashCode() { 
        System.out.println("判断hashcode"); 
        return 1; 
    } 

 

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

Java代码
  1. public class Test {    
  2.     
  3.     public static void main(String[] args) {    
  4.         Map<A,Object> map = new HashMap<A, Object>();    
  5.         map.put(new A(), new Object());    
  6.         map.put(new A(), new Object());    
  7.             
  8.         System.out.println(map.size());    
  9.     }    
  10.         
  11. }    

 

运行之后打印结果是: 

判断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不相等,则没有必要再调用 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。 

改写程序如下: 

import java.util.HashMap; 
import java.util.Map; 
 
 
class A { 
 
    @Override 
    public boolean equals(Object obj) { 
        System.out.println("判断equals"); 
        return true; 
    } 
 
    @Override 
    public int hashCode() { 
        System.out.println("判断hashcode"); 
        return 1; 
    } 

 
 
public class Test { 
 
    public static void main(String[] args) { 
        Map<A,Object> map = new HashMap<A, Object>(); 
        map.put(new A(), new Object()); 
        map.put(new A(), new Object()); 
         
        System.out.println(map.size()); 
    } 
     

 

运行之后打印结果是: 

判断hashcode 
判断hashcode 
判断equals 



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

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

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

 

 

 

 

分享到:
评论

相关推荐

    关于HashCode码的重复问题 两种验证实例

    1,如果两个对象相同,那么它们的hashCode值一定要相同; 2,如果两个对象的hashCode相同,它们并不一定相同 上面说的对象相同指的是用eqauls方法比较。 3,HashCode码不唯一

    【面试】hashCode与equals两者之间的关系 / == 和equals / 为什么要重写equals方法 / 重写equals /hashcode方法 / 为什么要重写hashCode方法

    如果两个对象相同(即用equals比较返回true),那么它们的hashCode值一定要相同!!!! 如果两个对象不同(即用equals比较返回false),那么它们的hashCode值可能相同也可能不同。 如果两个对象的hashCode相同...

    javaee_tedu_day09.zip

    ==比较两个对象的内存地址 如果想表示对象的内容相同,返回true,则重写此方法 hashCode 返回一个hash code码,Integer,内存地址有关的hash算法 如果equal返回true的话,hashCode相同,重写hashCode方法

    Java中hashCode的作用

    以下是关于HashCode的官方文档定义:  hashcode方法返回该对象的哈希码值。...  如果根据 equals(Object) 方法,两个对象是相等的,那么在两个对象中的每个对象上调用 hashCode 方法都必须生成相

    Java面试经典题,对JAVA面试很有帮助

    17.有没有可能两个不相等的对象有相同的hashcode 18.深拷贝和浅拷贝的区别是什么 19.final有哪些用法 20.static都有哪些用法 21.3*0.1 == 0.3返回值是什么 22.a=a+b与a+=b有什么区别吗 23.try catch finally,try里...

    java面试题.docx

    两个对象的 hashCode()相同,则 equals()也一定为 true,对吗? final 在 java 中有什么作用? java 中操作字符串都有哪些类?它们之间有什么区别? 如何将字符串反转? String 类的常用方法都有那些? 接口和抽象...

    Java 最常见的 208 道面试题:第一模块答案

    两个对象的 hashCode()相同,则 equals()也一定为 true,对 吗? final 在 java 中有什么作用? java 中的 Math.round(-1.5) 等于多少? String 属于基础的数据类型吗? java 中操作字符串都有哪些类?它们之间有...

    【05-面向对象(下)】

    •Object类提供的equals方法判断两个对象相等的标准与==完全相同。因此开发者通常需要重写equals方法。 类成员 •在java类里只能包含Field,方法,构造器,初始化块,内部类(接口、枚举)等5种成员。 用...

    architect-awesome-code

    如果对两个引用调用hashCode()会得到相同的结果。(默认情况下,Object的hashCode()返回对象的32位jvm内存地址。)因此,如果对象所属的类没有覆盖Object的hashCode(),指向同一对象的引用调用hashCode的值相等,而...

    软件构造心得(10):编写等价性判断时重写Hashcode的原因:理论与性能分析

    一句话概括,因为我们要保证,在我们的ADT被使用hash值作为hashset等数据结构的部分时(绝大多数情况这是不可被避免的),避免由于预设的hash值(预设的是地址,unpredictable)和我们判定两个对象等价的

    -互联网Java面试重点难点.rar

    3. 两个对象的 hashCode()相同,则 equals()也一定为 true,对吗? 4. final 在 java 中有什么作用? 5. java 中的 Math.round(-1.5) 等于多少? 6. String 属于基础的数据类型吗? 7. java 中操作字符串都有...

    java2022面试宝典

    一.JDK、JRE、JVM之间的区别 JDK(Java SE Development Ki),Java标准开发包,它提供了编译、运行Jlva程序所需的各种工具和资源,包括Java编译...比如: 在Java的一些集合类的实现中,在比较两个对象是否相等时,会根据上

    超实用的面试题整理

    · 如果两个key有相同的hash值,他们会被放在table数组的同一个桶里面。 · key的equals()方法用来确保key的唯一性。 · value对象的equals()和hashcode()方法根本一点用也没有。 HashMap是基于哈希表实现的,每...

    8张图理解Java

    一图胜千言,下面图解均来自Program Creek 网站的Java教程,目前它们拥有多的票选。如果图解没有阐明问题,那么你可以借助它的标题来一窥究竟。 ...  如果两个对象的哈希值相同,但他们未必相等(e

    Java面试宝典Beta6.0.pdf

    equals()相等的两个对象,hashCode()一定相等;反过来:hashCode()不等,一定能推出equals()也不等;hashCode()相等,equals()可能相等,也可能不等。 6. String类继承 String类是final类,不可以被继承。继承...

    史上最全java面试,103项重点知识,带目录

    3. 两个对象的 hashCode()相同,则 equals()也一定为 true,对吗? 3 4. final 在 java 中有什么作用? 4 5. java 中的 Math.round(-1.5) 等于多少? 4 6. String 属于基础的数据类型吗? 4 7. java 中操作字符串都...

    Java常见面试题208道.docx

    3.两个对象的 hashCode()相同,则 equals()也一定为 true,对吗? 4.final 在 java 中有什么作用? 5.java 中的 Math.round(-1.5) 等于多少? 6.String 属于基础的数据类型吗? 7.java 中操作字符串都有哪些类?它们...

    Java回顾(六)Set集合

    不同的英文单词的hashCode值是不相同的,但两个不同的中文字符返回的hashCode值有些确是相同的,原因是因为String里面重写了Object中的这个hashCode方法。 public class SetTest { public static void main(String[]...

    java中级面试题(自己汇总)

    * JDK7用头插是考虑到了一个所谓的热点数据的点(新插入的数据可能会更早用到),但这其实是个伪命题,因为JDK7中rehash的时候,旧链表迁移新链表的时候,如果在新表的数组索引位置相同,则链表元素会倒置(就是因为...

    sesvc.exe 阿萨德

    从这两个核心方法(get/put)可以看出 1.8 中对大链表做了优化,修改为红黑树之后查询效率直接提高到了 O(logn)。 但是 HashMap 原有的问题也都存在,比如在并发场景下使用时容易出现死循环。 final HashMap, ...

Global site tag (gtag.js) - Google Analytics