`
ywu
  • 浏览: 455728 次
  • 性别: Icon_minigender_1
  • 来自: 无锡
社区版块
存档分类
最新评论

hashCode

阅读更多

说明:

本系列博客是本人在工作中遇到的一些问题的整理,其中有些资料来源网络博客,有些信息来自出版的书籍,掺杂一些个人的猜想及验证,总结,主要目的是方便知识的查看,并非纯原创。本系列博客会不断更新。原创不容易,支持原创。对于参考的一些其他博客,会尽量把博客地址列在博客的后面,以方便知识的查看。

 

本篇博客可以看做是《Effective Java中文版2版》第三章(对于所有对象都通用的方法)第九条(覆盖equals时总要覆盖hashCode)的读书笔记,其中掺杂了一些个人的想法及验证。

 

我们来看下Object类中hashCode方法的实现:

 

public native int hashCode();

 

这是一个本地方法

 

java规范对其描述是这样的:

1、在应用程序的执行期间,只要对象的equals方法的比较操作所用到的信息没有被修改,那么对这同一个对象调用多次,hashCode方法都必须始终如一地返回同一个整数。在同一个应用程序的多次执行过程中,每次执行所返回的整数可以不一致。

2、如果两个对象根据equals方法比较是相等的,那么调用这两个对象中任意一个对象的hashCode方法都必须产生相同的整数结果。

3、如果两个对象根据equals方法比较是不相等的,那么调用这两个对象中国任意一个对象的hashCode方法,不一定产生不同的整数结果。但是给不相等的对象产生不同的整数结果,有可能提高散列表的性能。

 

书中的一个例子:

/**

 * Created with IntelliJ IDEA.

 * User: yejunwu123@gmail.com

 * Date: 2014-08-20 13:27

 * Description:

 */

public class PhoneNumber {

    private short areaCode;

    private short prefix;

    private short lineNumber;

    public PhoneNumber(int areaCode,int prefix,int lineNumber){

        this.areaCode = (short)areaCode;

        this.prefix = (short)prefix;

        this.lineNumber = (short)lineNumber;

    }

}

此处没有覆盖hashCodeequals方法,看下测试:

/**

 * Created with IntelliJ IDEA.

 * User: yejunwu123@gmail.com

 * Date: 2014-08-19 23:41

 * Description:

 */

public class HashCodeTest {

    public static void main(String[] args) {

        PhoneNumber phoneNumber1 = new PhoneNumber(1,2,3);

        PhoneNumber phoneNumber2 = new PhoneNumber(1,2,3);

        System.out.println("phoneNumber1.hashCode:" + phoneNumber1.hashCode());

        System.out.println("phoneNumber2.hashCode:" + phoneNumber2.hashCode());

    }

}

控制台输出:

phoneNumber1.hashCode:24355087

phoneNumber2.hashCode:5442986

类的不同实例,他们的hashCode不同

 

我们来加上equals方法:

@Override   

public boolean equals(Object obj) {

    if (obj == this){

        return true;

    }

    if (!(obj instanceof PhoneNumber)){

        return false;

    }

    PhoneNumber p = (PhoneNumber)obj;

    return this.areaCode == p.areaCode

                && this.prefix == p.prefix

                && this.lineNumber == p.lineNumber;

}

 

看下如下测试:

public class HashCodeTest {

    public static void main(String[] args) {

        PhoneNumber phoneNumber1 = new PhoneNumber(1,2,3);

        PhoneNumber phoneNumber2 = new PhoneNumber(1,2,3);

        System.out.println("phoneNumber1.hashCode:" + phoneNumber1.hashCode());

        System.out.println("phoneNumber2.hashCode:" + phoneNumber2.hashCode());

        System.out.println("------------------");

        System.out.println("phoneNumber1.equals(phoneNumber2):" + phoneNumber1.equals(phoneNumber2));

        Map<PhoneNumber,String> map = new HashMap<PhoneNumber, String>();

        map.put(phoneNumber1,"zhangsan");

        System.out.println("map contains phoneNumber1:" + map.containsKey(phoneNumber2));

        String name = map.get(phoneNumber2);

        System.out.println("name:" + name);

    }

}

 

创建PhoneNumber 的两个实例,先比较他们的equals,然后将第一个实例加入hashMap,然后判断HashMapcontainsKey方法及get方法(入参是第二个实例),控制台输出如下:

phoneNumber1.hashCode:24355087

phoneNumber2.hashCode:5442986

------------------

phoneNumber1.equals(phoneNumber2):true

map contains phoneNumber1:false

name:null

 

导致这个原因是因为覆盖了equals方法,没有同时覆盖hashCode方法,HashMap实现在putgetcontainsKey等方法时会计算keyhash,具体HashMap的原理可以参看博客:

http://www.cnblogs.com/xwdreamer/archive/2012/06/03/2532832.html

 

已经讲得很详细了,不重复劳动了。

 

书中提出的一种简单的hashCode生成方式如下:

1、把某个非0的常数值,比如说17,保存在一个名为resultint类型的变量中;

2、对于对象中的每个关键域f(equals方法中涉及的每个域),完成以下步骤:

  a.为该域计算int类型的散列码c

    .如果该类型是boolean类型,则计算(f ? 1 : 0);

    .如果该域是bytecharshortint类型,则计算(int)f

    .如果该域是long类型,则计算(int)(f ^ (f >>> 32));

    .如果该域是float类型,则计算Float.floatToIntBits(f);

    .如果该域是double类型,则计算Double.doubleToLongBits(f),然后按照步骤

          2.a.,为得到的long类型的值计算散列值;

    .如果该域是一个对象引用,并且该类的equals方法通过递归的调用equals的方式

          来比较这个域,则同样为这个域递归的调用hashCode,如果这个域的值为null,

          返回0

    .如果该域是一个数组,则要把每一个元素当做单独的域来处理。如果数组域中 

          的每个元素都很重要,可以使用Arrays.hashCode方法。

  b.按照如下公式,2.a中计算得到的散列码c合并到result中:

      result = 31 * result + c;

3、返回result

以上规则能记住当然最好,但是工作中我们用的IDE往往为我们提供了这些功能,以下代码是在IDEA中自动生成的:

@Override

public int hashCode() {

    int result = (int) areaCode;

    result = 31 * result + (int) prefix;

    result = 31 * result + (int) lineNumber;

    return result;

}

 

跟以上规则是类似的。

 

再来运行下之前的测试:

phoneNumber1.hashCode:1026

phoneNumber2.hashCode:1026

------------------

phoneNumber1.equals(phoneNumber2):true

map contains phoneNumber1:true

name:zhangsan

 

我们来看下如下的测试:

/**

 * Created with IntelliJ IDEA.

 * User: yejunwu123@gmail.com

 * Date: 2014-08-20 15:25

 * Description:

 */

public class PhoneNumberTest {

    public static void main(String[] args) {

        PhoneNumber phoneNumber1 = new PhoneNumber(1,2,3);

 

        Map<PhoneNumber,String> map = new HashMap<PhoneNumber, String>();

        map.put(phoneNumber1,"zhangsan");

 

        System.out.println("map contains PhoneNumber(1,2,3) before update:" + map.containsKey(new PhoneNumber(1,2,3)));

 

        System.out.println("----------------------");

 

        for (PhoneNumber phoneNumber : map.keySet()){

            System.out.println("before update hash code:" + phoneNumber.hashCode());

            phoneNumber.setAreaCode((short) 4);

            System.out.println("after update hash code:" + phoneNumber.hashCode());

        }

 

        for (PhoneNumber phoneNumber : map.keySet()){

            System.out.println(phoneNumber.getAreaCode() + " " + phoneNumber.getPrefix() + " " + phoneNumber.getLineNumber() + " " + phoneNumber.hashCode());

        }

 

        System.out.println("map contains PhoneNumber(1,2,3) after update:" + map.containsKey(new PhoneNumber(1,2,3)));

        System.out.println("##############");

        System.out.println("map contains PhoneNumber(4,2,3) after update:" + map.containsKey(new PhoneNumber(4,2,3)));

    }

}

 

phoneNumber1 加入HashMap,然后判断map.containsKey(new PhoneNumber(1,2,3)),然后对HashMap遍历,修改keyareacode属性,以改变keyhashCode,然后再去判断map.containsKey(new PhoneNumber(1,2,3))map.containsKey(new PhoneNumber(4,2,3))

 

看下输出结果:

map contains PhoneNumber(1,2,3) before update:true

----------------------

before update hash code:1026

after update hash code:3909

4 2 3 3909

map contains PhoneNumber(1,2,3) after update:false

##############

map contains PhoneNumber(4,2,3) after update:false

 

后面两次比较都是返回false

原因:

for (PhoneNumber phoneNumber : map.keySet()){           

    System.out.println("before update hash code:" + phoneNumber.hashCode());

    phoneNumber.setAreaCode((short) 4);

    System.out.println("after update hash code:" + phoneNumber.hashCode());

}

 

这段代码执行过后,因为mapkeyareaCode值已经修改,在判断map.containsKey(new PhoneNumber(1,2,3)),由于equals方法返回false导致contains方法返回false;而在判断map.containsKey(new PhoneNumber(4,2,3))返回false是因为mapkeyhashCodenew PhoneNumber(4,2,3)hashCode值不一样,这点在前文提到的博客中有说明,map中的entry中的key会缓存计算后的hash,详细信息可以参看上文提到的博客。

 

hashCodeequals方法还是很重要的,在理解hibernate的数据库一致性与实体一致性时有涉及到。

分享到:
评论

相关推荐

    HashCode的用法详解

    hashCode 的用法详解 hashCode 是一种用于查找和排序的机制,在数据结构中 plays a crucial role。下面我们将对 hashCode 的用法进行详细的解释。 hashCode 的作用 hashCode 的主要作用是用于查找和排序。在查找...

    深入 HashCode 方法~

    ### 深入理解 HashCode 方法 #### 一、HashCode 的基本概念与作用 在 Java 编程语言中,`HashCode` 是一个非常重要且基础的概念。简单来说,`HashCode` 是一个整数值,用于快速定位对象的位置。在 Java 中,每一个...

    重写equals和hashcode方法_equals_重写equals和hashcode方法_

    在Java编程语言中,`equals()` 和 `hashCode()` 方法是Object类中的两个核心方法,所有类都默认继承自Object类。这两个方法在处理对象比较和集合操作时起着至关重要的作用。当我们创建自定义类并需要对对象进行精确...

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

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

    equals与hashCode方法讲解

    equals 与 hashCode 方法讲解 equals 方法和 hashCode 方法是 Java 语言中两个重要的方法,它们都是在 Object 类中定义的。equals 方法用于比较两个对象是否相等,而 hashCode 方法用于返回对象的哈希码。 在 Java...

    java中Hashcode的作用.docx

    "Java中Hashcode的作用" Hashcode是Java编程语言中一个非常重要的概念,它在equals方法中扮演着关键角色。在Java中,每个对象都具有一个独特的Hashcode,它可以用来标识对象的身份。但是Hashcode是什么?它是如何...

    HashCode相同equals不同的2位字符集合算法

    在Java编程语言中,`hashCode()` 和 `equals()` 是两个非常重要的方法,它们主要用于对象的比较和哈希表(如HashMap)的操作。标题提到的"HashCode相同equals不同的2位字符集合算法"涉及到的是一个特定场景:两个...

    java中hashcode()和equals()方法详解

    ### Java中`hashCode()`与`equals()`方法详解 #### 前言 在Java编程语言中,`hashCode()`和`equals()`方法是非常重要的概念,它们不仅对于深入理解Java内存管理至关重要,也是实现自定义类的关键部分之一。本文将...

    利用反射绕过编译器和hashcode高级应用

    本主题将深入探讨如何利用反射技术绕过编译器的一些限制,并介绍hashcode在高级应用中的用法。 首先,让我们理解反射的基本概念。在Java中,反射提供了一种方式,使我们能够在运行时动态地获取类的信息(如类名、...

    Java基础加强_ArrayList_HashSet的比较及Hashcode分析

    本篇将深入探讨ArrayList与HashSet的区别,并分析Hashcode在其中的作用。 ArrayList是基于动态数组实现的,它提供了按索引访问元素的能力,就像在数组中一样。由于内部维护了一个数组,ArrayList保证了元素的顺序性...

    hashcode和equals方法

    equals()和hashcode()这两个方法都是从object类中继承过来的。当String 、Math、还有Integer、Double。。。。等这些封装类在使用equals()方法时,已经覆盖了object类的equals()方法.

    复写hashCode()方法,和equasl()方法

    ### 复写`hashCode()`方法与`equals()`方法 在Java编程中,`hashCode()`方法与`equals()`方法是对象比较中的两个非常重要的方法。它们主要用于判断对象是否相等以及对象的散列值,这对于集合类(如`HashSet`)来说...

    java中hashcode()和equals()的详解

    在Java编程语言中,`hashCode()`和`equals()`方法是对象身份验证的关键组成部分,它们主要用于对象的比较和哈希表(如HashMap、HashSet等)的操作。理解这两个方法的工作原理对于编写高效和可靠的代码至关重要。 ...

    解析Java对象的equals()和hashCode()的使用

    深入解析Java对象的equals()和hashCode()的使用 在Java语言中,equals()和hashCode()两个函数的使用是紧密配合的,你要是自己设计其中一个,就要设计另外一个。在多数情况下,这两个函数是不用考虑的,直接使用它们...

    关于hashCode()和equals()的本质区别和联系

    hashCode() 和 equals() 的本质区别和联系 Java 中的每个对象都有 hashCode() 和 equals() 方法,这两个方法的正确实现对于 Java 开发人员来说是非常重要的。本文将详细介绍 hashCode() 和 equals() 的本质区别和...

    hashcode的作用

    ### HashCode的作用详解 #### 一、HashCode的基本概念 在Java中,`hashCode()` 方法是 `Object` 类的一个重要成员方法,它返回一个整数,这个整数通常用来表示对象的哈希值。哈希值在Java集合框架中扮演着至关重要...

    Java_重写equals()和hashCode()

    在Java编程语言中,`equals()` 和 `hashCode()` 方法是对象的基本组成部分,它们在很多场景下都发挥着至关重要的作用。这两个方法与对象的相等性比较和哈希表(如HashMap、HashSet)的运作紧密相关。这篇博客将深入...

    equals,hashcode,toString

    在Java编程语言中,`equals()`, `hashCode()` 和 `toString()` 是三个非常重要的方法,它们主要用于对象的比较、哈希存储以及打印对象信息。这三个方法是Java对象的基础特性,对于理解和开发高质量的Java程序至关...

    hashcode()和equals()

    在Java编程语言中,`hashCode()` 和 `equals()` 方法是两个非常重要的概念,尤其是在处理对象比较和哈希表(如 `HashMap` 或 `HashSet`)时。这两个方法来源于 `Object` 类,是所有Java类的基类,因此,每个自定义类...

Global site tag (gtag.js) - Google Analytics