`
410063005
  • 浏览: 178052 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

Java学习之HashMap: 为什么要同时实现hashCode()和equals()

    博客分类:
  • java
 
阅读更多

问题来自Java编程思想(第四版第17章)的一个例子。

 

考虑一个天气预报系统,将Groundhog与Prediction对象联系起来。创建这两个类,使用Groundhog作为键,Prediction作为值。以下是来自书上的代码。

 

//: containers/Groundhog.java
// Looks plausible, but doesn't work as a HashMap key.

public class Groundhog {
  protected int number;
  public Groundhog(int n) { number = n; }
  public String toString() {
    return "Groundhog #" + number;
  }
} ///:~


//: containers/Prediction.java
// Predicting the weather with groundhogs.
import java.util.*;

public class Prediction {
  private static Random rand = new Random(47);
  private boolean shadow = rand.nextDouble() > 0.5;
  public String toString() {
    if(shadow)
      return "Six more weeks of Winter!";
    else
      return "Early Spring!";
  }
} ///:~


//: containers/SpringDetector.java
// What will the weather be?
import java.lang.reflect.*;
import java.util.*;
import static net.mindview.util.Print.*;

public class SpringDetector {
  // Uses a Groundhog or class derived from Groundhog:
  public static <T extends Groundhog>
  void detectSpring(Class<T> type) throws Exception {
    Constructor<T> ghog = type.getConstructor(int.class);
    Map<Groundhog,Prediction> map =
      new HashMap<Groundhog,Prediction>();
    for(int i = 0; i < 10; i++)
      map.put(ghog.newInstance(i), new Prediction());
    print("map = " + map);
    Groundhog gh = ghog.newInstance(3);
    print("Looking up prediction for " + gh);
    if(map.containsKey(gh))
      print(map.get(gh));
    else
      print("Key not found: " + gh);
  }
  public static void main(String[] args) throws Exception {
    detectSpring(Groundhog.class);
  }
} /* Output:
map = {Groundhog #3=Early Spring!, Groundhog #7=Early Spring!, Groundhog #5=Early Spring!, Groundhog #9=Six more weeks of Winter!, Groundhog #8=Six more weeks of Winter!, Groundhog #0=Six more weeks of Winter!, Groundhog #6=Early Spring!, Groundhog #4=Six more weeks of Winter!, Groundhog #1=Six more weeks of Winter!, Groundhog #2=Early Spring!}
Looking up prediction for Groundhog #3
Key not found: Groundhog #3
*///:~

 这段代码首先使用Groundhog和与之相关联的Prediction填充HashMap,然后打印此HashMap,以便可以观察它是否被填入了一些内容。 然后使用标识数字为3的Groundhog作为键,查找与之对应的Prediction。

 

这段代码不能正确工作。 它无法找到数字3这个键。 问题在于Groundhog自动继承自基类Object,所以这里使用Object的hashCode()方法生成散列码, 而它默认是使用对象的地址计算散列码。 因此, Groundhog(3)生成的第一个实例的散列码与由Groundhog(3)生成的第二个实例的散列码是不同的, 而我们使用后者查找,当然找不到。

 

可能你认为,只需要编写恰当的hashCode()方法覆盖版本即可。但是它仍然无法正常运行,除非你同时覆盖equals()方法,它也是Object的一部分。 HashMap使用equals()判断当前的键是否与表中存在的键相同。 

 

那么, 这里提出一个问题,为什么覆盖hashCode()方法的同时,还需要覆盖equals()方法

 

直接看HashMap的源码可以找到答案。主要是它的get(Object key)方法。

 

    public V get(Object key) {
        if (key == null)
            return getForNullKey();
        int hash = hash(key.hashCode());
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
                return e.value;
        }
        return null;
    }

 可以看到get(Object key)方法的过程其实很简单,遍历HashMap的内部维护的Entry数组,找到匹配的目标Entry并返回其value即可。 匹配的标准是:

  1. Entry的hash值是否与参数key的hash值相等。 (这要求我们必须正确实现作为key的对象的hashCode()方法)
  2. Entry的key是否与参数key为同一对象,或者Entry的key"等于"参数key。 (注意是否"等于", 是通过调用参数key的equals()方法来完成的, 这要求我们必须正确实现作为key的对象的equals()方法)

明白以上这两个关键点,就不难解释为什么为什么覆盖hashCode()方法的同时,还需要覆盖equals()方法

 

分享到:
评论

相关推荐

    Java理论与实践:hashCode()和equals()方法

    本文介绍了Java语言不直接支持关联数组,可以使用任何对象作为一个索引的数组,但在根Object类中使用 hashCode()方法明确表示期望广泛使用HashMap。理想情况下基于散列的容器提供有效插入和有效检索;直接在对象模式...

    equals 和 hashCode两者效果分析详解.docx

    原因是因为,在Java自带的容器HashMap和HashSet中, 都需同时要用到对象的hashCode()和equals()方法来进行判断,然后再插入删除元素,这点我们一会再谈。 那么我们还是单独来看hashCode(),为什么HashMap需要用到...

    java面试题.docx

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

    java8源码-java-start::seedling::seedling::seedling:学习Java语法过程中的一些案例

    hashCode与equals) (String和StringBuffer、StringBuilder的区别是什么?String为什么是不可变的?、什么是反射机制?反射机制的应用场景有哪些?......) (Arraylist 与 LinkedList 异同、ArrayList 与 Vector 区别...

    JAVA入门1.2.3:一个老鸟的JAVA学习心得 PART1(共3个)

    6.3 小结:Java其实是个类和对象的世界 152 6.4 习题 153 第7章 Java中的方法——给汽车丰富多彩的功能 154 教学视频:2小时55分钟 7.1 方法:让汽车动开动 154 7.1.1 引出问题:开动汽车 154 7.1.2 那么,...

    实验05 Java集合.doc

    注意:因为Person类是自定义类,需要重写hashCode()方法和equals()方法,并规定只有姓名和身份证号都相等,则对象相等。 其中计算哈希码的算法:(31 + ((name == null) ? 0 : name.hashCode()))*31 + id (注:...

    Java入门1·2·3:一个老鸟的Java学习心得.PART3(共3个)

    6.3 小结:Java其实是个类和对象的世界 152 6.4 习题 153 第7章 Java中的方法——给汽车丰富多彩的功能 154 教学视频:2小时55分钟 7.1 方法:让汽车动开动 154 7.1.1 引出问题:开动汽车 154 7.1.2 那么,...

    Java面试题.docx

    1、java中==和equals和hashCode的区别 2、int与integer的区别 3、String、StringBuffer、StringBuilder区别 4、什么是内部类?内部类的作用 5、进程和线程的区别 6、final,finally,finalize的区别 7、...

    2018秋招java笔试题-javapass:java深入学习资料集锦

    2018秋招java笔试题 目录 :hot_beverage: Java Java/J2EE 基础 Java 集合框架 Java 多线程 Java ...Java ...最最最常见的Java面试题总结 这里会分享一些出现频率极其极其高的面试题,...区别、HashMap的底层实现、HashMap 和

    HashTable和HashMap的区别_动力节点Java学院整理

    HashSet实现了Set接口,它不允许集合中有重复的值,当我们提到HashSet时,第一件事情就是在将对象存储在HashSet之前,要先确保对象重写equals()和hashCode()方法,这样才能比较对象的值是否相等,以确保set中没有...

    Java equals 方法与hashcode 方法的深入解析

    面试时经常会问起字符串比较相关的问题,比如:字符串比较时用的什么方法,内部实现如何?hashcode的作用,以及重写equal方法,为什么要重写hashcode方法?以下就为大家解答,需要的朋友可以参考下

    疯狂JAVA讲义

    学生提问:Java为什么要对这些数据进行缓存呢? 67 3.7.6 逻辑运算符 67 3.7.7 三目运算符 68 3.7.8 运算符的结合性和优先级 69 3.8 本章小结 70 本章练习 70 第4章 流程控制和数组 71 4.1 顺序结构 72 4.2 ...

    Java常见面试题208道.docx

    面试题包括以下十九部分:Java 基础、容器、多线程、反射、对象拷贝、Java Web 模块、异常、网络、设计模式、Spring/Spring MVC、Spring Boot/Spring Cloud、Hibernate、Mybatis、RabbitMQ、Kafka、Zookeeper、MySql...

    大厂真题之阿里云-Java实习生

    HashSet 是如何保证不重复的 向 HashSet 中 add ()元素时,判断元素是否存在的依据,不仅要比较hash值,同时还要结合 equles 方法比较。 HashSet 中的 add ()方法会使用 HashMap 的 add ()方法。以下是 HashSet 部分...

    Java容器.xmind

    底层哈希表,基于hashCode的equals的比较方式,线程不安全,存取速度快。 SortedSet 标记: interface TreeSet 标记: class 实现comparable接口,元素是以二叉树的形式存放的。线程不安全 CopyOnWriteArraySet 标记:...

    涵盖了90%以上的面试题

    为什么重写equals还要重写hashCode? 介绍一下volatile jdk1.5新特性 jdk1.7新特性 jdk1.8新特性 java语言有哪些优点? 同一个.java文件中是否可以有多个main方法 一个".java"源文件中是否可以包括多个类(不是内部类...

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

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

    2023年最新java面试大全

    【09期】说说hashCode() 和 equals() 之间的关系? 【10期】Redis 面试常见问答 【11期】分布式系统接口,如何避免表单的重复提交? 【12期】谈谈项目中单点登录的实现原理? 【13期】谈谈 Redis 的过期策略 ...

    jdk1.7和jdk1.8中hashmap区别

    HashMap基于哈希散列表实现 ,可以实现对数据的读写。将键值对传递给put方法时,它调用键对象的hashCode()方法来计算hashCode,然后找到相应的bucket位置(即数组)来储存值对象。当获取对象时,通过键对象的equals...

Global site tag (gtag.js) - Google Analytics