覆写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. juce中的Singleton

    说明上其实很明白,支持多线程,防止重复创建,同时支持如果删除以后就不在创建,利用局部静态变量进行标记.挺通用,看来下次写个c11版本的 //============================== ...

  2. Bootstrap之Button.js

    查看Button.js的源代码 +function ($) { 'use strict'; // BUTTON PUBLIC CLASS DEFINITION // ================= ...

  3. Python成长之路第一篇(2)-初识列表和元组

    可以将列表和元组当成普通的“数组”,他能保存任意数量任意类型的Python对象,和数组一样都是通过数字0索引访问元素,列表和元组可以存储不同类型的对象,列表和元组有几处重要区别.列表元素用([])包括 ...

  4. jquery ajax提交及请求

    jQuery.ajax({ url: dataURL, success: function(results) { var parsedJson = jQuery.parseJSON(results); ...

  5. JavaScript学习之—prototype

    一.利用prototype扩展String方法,去除字符前后空格: String.prototype.trim = function String$trim() { if (arguments.len ...

  6. [Django 1.5] Windows + Apache + wsgi配置

    基本步骤 下载安装Apache http://httpd.apache.org/download.cgi. 下载安装modwsgi 模块http://code.google.com/p/modwsgi ...

  7. Android第三方应用分享图文到微信朋友圈 & 微信回调通知分享状态

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAWQAAAKUCAIAAAC8A9XzAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWX ...

  8. 【POJ】2318 TOYS ——计算几何+二分

    TOYS Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 10281   Accepted: 4924 Description ...

  9. 全国计算机等级考试二级教程-C语言程序设计_第11章_对函数的进一步讨论

    无论传递什么参数,函数都有副本机制 改变一个变量,需要传入变量的地址 改变一个指针变量,需要传入指针变量的地址 //int add(int a, int b);挖取函数声明 //int ()(int ...

  10. iOS使用sqlite3原生语法进行增删改查以及FMDB的使用

    首先要导入libsqlite3.dylib并且加入头文件#import <sqlite3.h>,在进行增删改查之前还要先把数据库搞进去. 一种方法是从外面拷贝到程序里:http://www ...