详解Java中用于查找对象哈希码值的hashCode()函数

 更新时间:2016年05月27日 08:59:12   作者:冯立彬  
Java中入HashMap等一些键值对应的结构,基本上都可以用hashCode()来查找值,接下来我们就来详解Java中用于查找对象哈希码值的hashCode()函数:

理解
hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。
hashCode() 定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode() 函数。
虽然,每个Java类都包含hashCode() 函数。但是,仅仅当创建并某个“类的散列表”(关于“散列表”见下面说明)时,该类的hashCode() 才有用(作用是:确定该类的每一个对象在散列表中的位置;其它情况下(例如,创建类的单个对象,或者创建类的对象数组等等),类的hashCode() 没有作用。
上面的散列表,指的是:Java集合中本质是散列表的类,如HashMap,Hashtable,HashSet。
也就是说:hashCode() 在散列表中才有用,在其它情况下没用。在散列表中hashCode() 的作用是获取对象的散列码,进而确定该对象在散列表中的位置。
我们都知道,散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!
散列表的本质是通过数组实现的。当我们要获取散列表中的某个“值”时,实际上是要获取数组中的某个位置的元素。而数组的位置,就是通过“键”来获取的;更进一步说,数组的位置,是通过“键”对应的散列码计算得到的。
下面,我们以HashSet为例,来深入说明hashCode()的作用。
假设,HashSet中已经有1000个元素。当插入第1001个元素时,需要怎么处理?因为HashSet是Set集合,它允许有重复元素。
“将第1001个元素逐个的和前面1000个元素进行比较”?显然,这个效率是相等低下的。散列表很好的解决了这个问题,它根据元素的散列码计算出元素在散列表中的位置,然后将元素插入该位置即可。对于相同的元素,自然是只保存了一个。
由此可知,若两个元素相等,它们的散列码一定相等;但反过来确不一定。在散列表中,
1、如果两个对象相等,那么它们的hashCode()值一定要相同;
2、如果两个对象hashCode()相等,它们并不一定相等。
注意:这是在散列表中的情况。在非散列表中一定如此!

示例
我们来看一个具体的示例吧,

public class HashTest { 
  private int i; 
 
  public int getI() { 
    return i; 
  } 
 
  public void setI(int i) { 
    this.i = i; 
  } 
 
  public int hashCode() { 
    return i % 10; 
  } 
 
  public final static void main(String[] args) { 
    HashTest a = new HashTest(); 
    HashTest b = new HashTest(); 
    a.setI(1); 
    b.setI(1); 
    Set<HashTest> set = new HashSet<HashTest>(); 
    set.add(a); 
    set.add(b); 
    System.out.println(a.hashCode() == b.hashCode()); 
    System.out.println(a.equals(b)); 
    System.out.println(set); 
  } 
} 

这个输出的结果:

true 
false 
[com.ubs.sae.test.HashTest@1, com.ubs.sae.test.HashTest@1] 

以上这个示例,我们只是重写了hashCode方法,从上面的结果可以看出,虽然两个对象的hashCode相等,但是实际上两个对象并不是相等;,我们没有重写equals方法,那么就会调用object默认的equals方法,是比较两个对象的引用是不是相同,显示这是两个不同的对象,两个对象的引用肯定是不定的。这里我们将生成的对象放到了HashSet中,而HashSet中只能够存放唯一的对象,也就是相同的(适用于equals方法)的对象只会存放一个,但是这里实际上是两个对象a,b都被放到了HashSet中,这样HashSet就失去了他本身的意义了。
此时我们把equals方法给加上:

public class HashTest { 
  private int i; 
 
  public int getI() { 
    return i; 
  } 
 
  public void setI(int i) { 
    this.i = i; 
  } 
 
  <span style="color:#3366FF;"><strong>public boolean equals(Object object) { 
    if (object == null) { 
      return false; 
    } 
    if (object == this) { 
      return true; 
    } 
    if (!(object instanceof HashTest)) { 
      return false; 
    } 
    HashTest other = (HashTest) object; 
    if (other.getI() == this.getI()) { 
      return true; 
    } 
    return false; 
  }</strong></span> 
 
  public int hashCode() { 
    return i % 10; 
  } 
 
  public final static void main(String[] args) { 
    HashTest a = new HashTest(); 
    HashTest b = new HashTest(); 
    a.setI(1); 
    b.setI(1); 
    Set<HashTest> set = new HashSet<HashTest>(); 
    set.add(a); 
    set.add(b); 
    System.out.println(a.hashCode() == b.hashCode()); 
    System.out.println(a.equals(b)); 
    System.out.println(set); 
  } 
} 

此时得到的结果就会如下:

true 
true 
[com.ubs.sae.test.HashTest@1] 

从结果我们可以看出,现在两个对象就完全相等了,HashSet中也只存放了一份对象。

总结
1、hashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,hashCode是用来在散列存储结构中确定对象的存储地址的;

2、如果两个对象相同,就是适用于equals(java.lang.Object) 方法,那么这两个对象的hashCode一定要相同;

3、如果对象的equals方法被重写,那么对象的hashCode也尽量重写,并且产生hashCode使用的对象,一定要和equals方法中使用的一致,否则就会违反上面提到的第2点;

4、两个对象的hashCode相同,并不一定表示两个对象就相同,也就是不一定适用于equals(java.lang.Object) 方法,只能够说明这两个对象在散列存储结构中,如Hashtable,他们“存放在同一个篮子里”。

相关文章

  • Spring Cloud Stream分区分组原理图解

    Spring Cloud Stream分区分组原理图解

    这篇文章主要介绍了Spring Cloud Stream的分区和分组,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-03-03
  • Mybatis基于注解实现多表查询功能

    Mybatis基于注解实现多表查询功能

    这篇文章主要介绍了Mybatis基于注解实现多表查询功能,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-09-09
  • Java将List转换为String的几种方式

    Java将List转换为String的几种方式

    我们大家在实际开发中经常遇到List转为String字符串的情况,下面这篇文章主要给大家介绍了关于Java将List转换为String的几种方式,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-05-05
  • SpringCloud 服务注册IP错误的解决

    SpringCloud 服务注册IP错误的解决

    这篇文章主要介绍了SpringCloud 服务注册IP错误的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • Java动态调用类中方法代码

    Java动态调用类中方法代码

    这篇文章主要介绍了Java动态调用类中方法代码,需要的朋友可以参考下
    2014-02-02
  • Spring Boot与Redisson实时排行榜功能

    Spring Boot与Redisson实时排行榜功能

    排行榜功能是常见且重要的需求之一,本文主要介绍了Spring Boot与Redisson实时排行榜功能,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-05-05
  • SpringCloud实战之Feign声明式服务调用

    SpringCloud实战之Feign声明式服务调用

    这篇文章主要介绍了SpringCloud实战之Feign声明式服务调用,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-05-05
  • 浅谈java中Math.random()与java.util.random()的区别

    浅谈java中Math.random()与java.util.random()的区别

    下面小编就为大家带来一篇浅谈java中Math.random()与java.util.random()的区别。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-09-09
  • 基于Java 谈回调函数

    基于Java 谈回调函数

    回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所 指向的函数时,我们就说这是回调函数
    2017-05-05
  • 浅谈在JAVA项目中LOG4J的使用

    浅谈在JAVA项目中LOG4J的使用

    下面小编就为大家带来一篇浅谈在JAVA项目中LOG4J的使用。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-12-12

最新评论