覆写hashCode()

在明白了HashMap具有哪些功能,以及实现原理后,了解如何写一个hashCode()方法就更有意义了。当然,在HashMap中存取一个键值对涉及到的另外一个方法为equals (),因为该方法的覆写在高级特性已经讲解了。这里就不做过多的描述。

设计hashCode()时最重要的因素就是:无论何时,对同一个对象调用hashCode()都应该生成同样的值。如果在将一个对象用put()方法添加进HashMap时产生一个hashCode()值,而用get()取出时却产生了另外一个hashCode()值,那么就无法重新取得该对象了。所以,如果你的hashCode()方法依赖于对象中易变的数据,那用户就要小心了,因为此数据发生变化时,hashCode()就会产生一个不同的hash码,相当于产生了一个不同的“键”。

此外,也不应该使hashCode()依赖于具有唯一性的对象信息,尤其是使用this的值,这只能产生很糟糕的hashCode()。因为这样做无法生成一个新的“键”,使之与put()种原始的“键值对”中的“键”相同。例如,如果我们不覆写Object的hashCode()方法,那么调用该方法的时候,就会调用Object的hashCode()方法的默认实现。Object的hashCode()方法,返回的是当前对象的内存地址。下次如果我们需要取一个一样的“键”对应的键值对的时候,我们就无法得到一样的hashCode值了。因为我们后来创建的“键”对象已经不是存入HashMap中的那个内存地址的对象了。

我们看一个简单的例子,就能更加清楚的理解上面的意思。假定我们写了一个类:Person (人),我们判断一个对象“人”是否指向同一个人,只要知道这个人的身份证号一直就可以了。

先看我们没有实现hashCode的情况:

package c08.hashEx;

import java.util.*;

//身份证类

class Code{

   final int id;//身份证号码已经确认,不能改变

   Code(int i){

      id=i;

   }

   //身份号号码相同,则身份证相同

   public boolean equals(Object anObject) {

      if (anObject instanceof Code){

         Code other=(Code) anObject;

         return this.id==other.id;

      }

      return false;

   }

   public String toString() {

      return "身份证:"+id;

   } 

}

//人员信息类

class Person {

   Code id;// 身份证

   String name;// 姓名

   public Person(String name, Code id) {

      this.id=id;

      this.name=name;

   }

   //如果身份证号相同,就表示两个人是同一个人

   public boolean equals(Object anObject) {

      if (anObject instanceof Person){

         Person other=(Person) anObject;

         return this.id.equals(other.id);

      }

      return false;

   }

   public String toString() {

      return "姓名:"+name+" 身份证:"+id.id+"\n";

   }

}

public class HashCodeEx {

   public static void main(String[] args) {

      HashMap map=new HashMap();

      Person p1=new Person("张三",new Code(123));

      map.put(p1.id,p1);//我们根据身份证来作为key值存放到Map中

      Person p2=new Person("李四",new Code(456));

      map.put(p2.id,p2);

      Person p3=new Person("王二",new Code(789));

      map.put(p3.id,p3);

      System.out.println("HashMap 中存放的人员信息:\n"+map);

      // 张三 改名为:张山 但是还是同一个人。

      Person p4=new Person("张山",new Code(123));

      map.put(p4.id,p4);

      System.out.println("张三改名后 HashMap 中存放的人员信息:\n"+map);

      //查找身份证为:123 的人员信息

      System.out.println("查找身份证为:123 的人员信息:"+map.get(new Code(123)));

   }

}

运行结果为:

HashMap 中存放的人员信息:

{身份证:456=姓名:李四 身份证:456

, 身份证:123=姓名:张三 身份证:123

, 身份证:789=姓名:王二 身份证:789

}

张三改名后 HashMap 中存放的人员信息:

{身份证:456=姓名:李四 身份证:456

, 身份证:123=姓名:张三 身份证:123

, 身份证:123=姓名:张山 身份证:123

, 身份证:789=姓名:王二 身份证:789

}

查找身份证为:123 的人员信息:null

上面的例子的演示的是,我们在一个HashMap中存放了一些人员的信息。并以这些人员的身份证最为人员的“键”。当有的人员的姓名修改了的情况下,我们需要更新这个HashMap。同时假如我们知道某个身份证号,想了解这个身份证号对应的人员信息如何,我们也可以根据这个身份证号在HashMap中得到对应的信息。

而例子的输出结果表示,我们所做的更新和查找操作都失败了。失败的原因就是我们的身份证类:Code 没有覆写hashCode()方法。这个时候,当查找一样的身份证号码的键值对的时候,使用的是默认的对象的内存地址来进行定位。这样,后面的所有的身份证号对象new Code(123) 产生的hashCode()值都是不一样的。所以导致操作失败。

下面,我们给Code类加上hashCode()方法,然后再运行一下程序看看:

//身份证类

class Code{

   final int id;//身份证号码已经确认,不能改变

   Code(int i){

      id=i;

   }

   //身份号号码相同,则身份证相同

   public boolean equals(Object anObject) {

      if (anObject instanceof Code){

         Code other=(Code) anObject;

         return this.id==other.id;

      }

      return false;

   }

   public String toString() {

      return "身份证:"+id;

   } 

   //覆写hashCode方法,并使用身份证号作为hash值

   public int hashCode(){

      return id;

   }

}

再次执行上面的HashCodeEx 的结果就为:

HashMap 中存放的人员信息:

{身份证:456=姓名:李四 身份证:456

, 身份证:789=姓名:王二 身份证:789

, 身份证:123=姓名:张三 身份证:123

}

张三改名后 HashMap 中存放的人员信息:

{身份证:456=姓名:李四 身份证:456

, 身份证:789=姓名:王二 身份证:789

, 身份证:123=姓名:张山 身份证:123

}

查找身份证为:123 的人员信息:姓名:张山 身份证:123

这个时候,我们发现。我们想要做的更新和查找操作都成功了。

对于Map部分的使用和实现,主要就是需要注意存放“键值对”中的对象的equals()方法和hashCode()方法的覆写。如果需要使用到排序的话,那么还需要实现Comparable 接口中的compareTo() 方法。我们需要注意Map中的“键”是不能重复的,而是否重复的判断,是通过调用“键”对象的equals()方法来决定的。而在HashMap中查找和存取“键值对”是同时使用hashCode()方法和equals()方法来决定的。

J2SE知识点摘记(二十四)的更多相关文章

  1. J2SE知识点摘记(二十六)

    为了用“集合框架”的额外部分把排序支持添加到 Java 2 SDK,版本 1.2,核心 Java 库作了许多更改.像 String 和 Integer 类如今实现 Comparable 接口以提供自然 ...

  2. J2SE知识点摘记(二十五)

    Set 1.5.1        概述 Java 中的Set和正好和数学上直观的集(set)的概念是相同的.Set最大的特性就是不允许在其中存放的元素是重复的.根据这个特点,我们就可以使用Set 这个 ...

  3. J2SE知识点摘记(二十二)

    Map 1.4.1        概述 数学中的映射关系在Java中就是通过Map来实现的.它表示,里面存储的元素是一个对(pair),我们通过一个对象,可以在这个映射关系中找到另外一个和这个对象相关 ...

  4. J2SE知识点摘记(二十)

    List 1.3.1        概述 前面我们讲述的Collection接口实际上并没有直接的实现类.而List是容器的一种,表示列表的意思.当我们不知道存储的数据有多少的情况,我们就可以使用Li ...

  5. J2SE知识点摘记(二十三)

    我们简单介绍一下这个接口: 1.4.3        Comparable 接口 在 java.lang 包中,Comparable 接口适用于一个类有自然顺序的时候.假定对象集合是同一类型,该接口允 ...

  6. J2SE知识点摘记(二)

    1.    对象的声明 "类名 对象名 = new 类名();"例子:Person P;//先声明一个Person类的对象p p=new Person();//用new关键字实例化 ...

  7. J2SE知识点摘记(二十一)

    实现原理 前面已经提了一下Collection的实现基础都是基于数组的.下面我们就已ArrayList 为例,简单分析一下ArrayList 列表的实现方式.首先,先看下它的构造函数. 下列表格是在S ...

  8. 小小知识点(二十四)什么是5G

    转自 https://www.ifanr.com/1149419 一个简单且神奇的公式 今天的故事,从一个公式开始讲起.这是一个既简单又神奇的公式.说它简单,是因为它一共只有 3 个字母.而说它神奇, ...

  9. JAVA之旅(二十四)——I/O流,字符流,FileWriter,IOException,文件续写,FileReader,小练习

    JAVA之旅(二十四)--I/O流,字符流,FileWriter,IOException,文件续写,FileReader,小练习 JAVA之旅林林总总也是写了二十多篇了,我们今天终于是接触到了I/O了 ...

随机推荐

  1. C/S ASP.NET页面传值汇总

    一. QueryString是一种非常简单的传值方式,他可以将传送的值显示在浏览器的地址栏中.如果是传递一个或多个安全性要求不高或是结构简单的数值时,可以使用这个方法.但是对于传递数组或对象的话,就不 ...

  2. HMM模型实例 mahout官网上的案例

    原理:http://www.cnblogs.com/CheeseZH/p/4229910.html Example To build a Hidden Markov Model and use it ...

  3. python学习笔记:python数字

    一.数字类型分类 数字提供了标量存储和直接访问,它是不可更改类型,也就是说变更数字的值会产生新的对象.python的对象模型与常规对象模型有些不同,对数字对象的更新,实际上是生成了一个新的数值对象,并 ...

  4. PARTITION(number theory) ALSO Explosive number in codewars

    问题出于codewars,简言之:寻找和为一个整数的的不同整数组合.https://en.wikipedia.org/wiki/Partition_(number_theory) 例如:和为6的整数组 ...

  5. JSON对象和字符串的互相转换

    1.JSON.parse; 作用:将JavaScript对象表示法的JSON字符串转换为对象(字符串转对象) 语法:JSON.parse(text [, reviver]) text    必选. 一 ...

  6. JavaScript学习笔记(二)原型

    JavaScript不包含传统的类继承模型,而是使用prototype原型模型.JavaScript使用原型链的继承方式. function Foo() { this.value = 42; } Fo ...

  7. svn检出maven工程到eclipse里面,部署到tomcat的步骤

    1. 首先import project from svn 2.检出项目后,如果是多模块的maven项目,在子模块右击,import as project 3.右击项目,properties->d ...

  8. Robot Framework语法学习(一)

    Robot Framework语法学习: 一.变量的声明.赋值与使用 1.变量标识符:每个变量都可以用  变量标识符 ${变量名} 来表示. 2.变量声明:可以在TestSuite上点右键或者在Edi ...

  9. Oracle EBS-SQL (WIP-10):检查车间任务状态“完成”但未发料数据.sql

    select WE.WIP_ENTITY_NAME                                               任务号,         MFG_LOOKUPS_WJS ...

  10. 百用随身系统 Veket Linux

    Veket Linux 是一个随身的可装在U盘的Linux操作系统. 特点:1,随身系统,装在U盘可走遍天下,它几乎支持“所有”的电脑,就我所接触得到的电脑它都支持并成功驱动,就这十多年的安装的电脑都 ...