hashCode()方法基本实现

  hashCode方法是Java的Object类所定义的几个基本方法之一。我们可以深入到Object类的源码中去查看:

  public native int hashCode();

  其中native关键字表明这个函数是由非java语言来实现的,这个函数的功能就是返回这个对象在内存中的地址。

hashCode()方法的应用

  大部分类都会重新覆写一下hashCode方法,原因有很多。那么这个方法主要被应用在什么场景下呢?一个非常重要的应用就是当我们处理散列集合类的时候,比如HashSet,HashMap,HashTable。

  java之所以要设计这三种散列集合类,目的就是能够在常数时间复杂度O(1)下,完成“确定一个元素是否在一个集合中”,“插入元素”,“删除元素”等操作。采取的思路就是使用哈希表。这是一个我们经常见到的数据结构。我们在实现一个哈希表时最重要的一步操作就是通过这个元素的关键字来计算它的哈希值,从而就可以通过这个哈希值来查询哈希表,看看这个元素是否在集合中。那么hashCode方法的作用就是提供这个元素的关键字。

  我们可以具体看一下HashMap里面几个重要方法的实现:

     public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
} modCount++;
addEntry(hash, key, value, i);
return null;
}

  这个方法是向集合中插入一个新的“键值对”元素,其中:

    第4行首先通过hashCode计算这个元素的关键字。

    第4行调用hash()函数,计算这个关键字的哈希值。

    第5行计算这个哈希值对应于哈希表中的哪个位置,HashMap会维护一个哈希表 Entry[] table。每一个位置对应一个哈希值,并且任意一个Entry[i]元素其实是一个链表头,这个链表中存放着在集合中所有哈希值为i的元素。

    第6~13行就要遍历这个table[i]所管理的链表,并且查询这个链表中是否已经存在了关键字为k的元素。如果查找到了,则用新的value去覆盖旧的value,并且函数会把旧的value返回。

    第16~18行,如果运行到这里代表这个一个全新的元素,此时就把它加到集合中。

  另外其他的方法比如get,也会运用到hashCode方法。

hashCode()方法的覆写

  hashCode()方法在Object的实现中,是返回对象的内存地址,但是通常在第一个类的时候,我们经常会覆写hashCode()方法,比如在Integer类中,hashCode()就是返回这个Integer的值。

  所以这就会造成多个问题,或者说多个结论:

  1. 如果两个引用hashCode()返回的值相同,并不能代表这两个引用指向的是同一个对象。

  2. 如果两个引用指向的是同一个对象,则它们的hashCode()方法一定返回一样的值。

  3. 如果两个引用的hashCode值返回的是不一样的值,则这两个引用一定指向的不是同一个对象。

   实例1:

     public static void main(String[] args) {
Integer i1 = new Integer(2);
Integer i2 = new Integer(2);
System.out.println("i1 hashCode is "+i1.hashCode()+" i2 hashCode is "+i2.hashCode());
if(i1.hashCode() == i2.hashCode())
System.out.println("i1, i2 have same hashCode");
else
System.out.println("i1, i2 have different hashCode");
if(i1 == i2)
System.out.println("i1, i2 are same");
else
System.out.println("i1, i2 are different");
}

  这个例子就是说明结论1的,i1,i2都是Integer对象,且值都是2,Integer的hashCode方法返回的就是Integer的值,所以它们的hashCode相同,但是i1和i2指向的是不同对象,所以最后输出的结果是:

i1 hashCode is 2 i2 hashCode is 2
i1, i2 have same hashCode
i1, i2 are different

hashCode()方法和equals()方法配合使用

  在自定义新的类时,经常会覆写equals方法,但是这里要注意,java有个规定:如果你覆写了某个类的equals方法,那么你一定也要覆写它的hashCode()方法。覆写的原则就是保证如果两个引用调用equals方法时如果返回true,则这两个引用的hashCode()的返回值也需要是一样的。

  为什么要这样做,我们可以看一下下面的这种情况:

 public class test {
public static void main(String[] args) {
People p1 = new People("zzq", 24);
Map<People, Integer> map = new HashMap<People, Integer>();
map.put(p1, 1);
System.out.println(map.get(new People("zzq", 24)));
}
} class People {
String name;
int age; public People(String name, int age) {
this.name = name;
this.age = age;
} public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}

  这个类中定义了一个其他的类,People。并且新建了一个HashMap<People, Integer>。并且将一个新建的people对象加入其中,然后我们想按照那个People对象的值新建另一个同样值的对象new People("zzq", 24),并且通过新建的对象找到它的值value。但是结果返回为null。我们希望它能够返回1。

  为什么这里不可以?主要就是因为没有实现hashCode()方法,此时这个类的hashCode方法仍旧是返回内存地址,所以二者的hashCode不同。

  此时我们可以看下 get() 方法

     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;
}

  其中首先就是第3行,首先计算出你要找的元素的关键字,以及这个关键字的hashCode,由于此时hashCode是内存地址,所以一开始存入的p1,和后来在get函数输入参数列表中新建的new People()对象,是具有不同的hashCode的。自然得出的hash值也是不一致的,所以一定找不到存放p1的那个哈希表项。所以一定会返回null。

  如果我覆写一下hashCode()方法和equals()方法:

     @Override
public int hashCode() {
return name.hashCode()+this.age;
} @Override
public boolean equals(Object obj) {
return this.name.equals(((People)obj).name) && this.age == ((People)obj).age;
}

  此时程序就会返回1了。

Java基础知识点2:hashCode()方法的更多相关文章

  1. Java基础知识点(三)

    前言:准备将Java基础知识点总结成一个系列,用于平常复习并加深理解.每篇尽量做到短小精悍,便于阅读. 1.Math类中相关函数 Math.floor(x):返回不大于x的最大整数.eg:Math.f ...

  2. Java基础知识点(一)

    前言:本篇随笔,主要记录Java的基础知识点,不管是用于项目或者面试中,笔者认为都非常有用,所以将持续更新...... 1.Java的访问权限 Java中有四种访问权限:默认访问权限.public.p ...

  3. JAVA基础知识点总结(全集)

    1.JAVA简介 1.1java体系结构:j2se,javaweb,j2ee 1.2java特点:平台无关(虚拟机),垃圾回收(使得java更加稳定) 1.3 JDK与JRE,JDK:java开发环境 ...

  4. JAVA基础(1)之hashCode()

    JAVA基础(1)之hashCode() 看到一篇关于hashCode的文章(),写的很详细明白,瞬间有种恍然大悟的感觉,所以特地转过来.原文:http://blog.csdn.net/fenglib ...

  5. Java基础知识点(四)

    前言:记录Java基础知识点,方便熟悉与掌握. 1.面向对象的"六原则一法则" “六原则一法则”:单一职责原则.开闭原则.依赖倒转原则.里氏替换原则.接口隔离原则.合成聚合复用原则 ...

  6. Java基础知识点(二)

    前言:Java的基础知识点不能间断. 1.Array和ArrayList的区别 关于Array的用法,参看:http://blog.csdn.net/b_11111/article/details/5 ...

  7. Java基础知识点总结

    前言 本文主要是我之前复习Java基础原理过程中写的Java基础知识点总结.Java的知识点其实非常多,并且有些知识点比较难以理解,有时候我们自以为理解了某些内容,其实可能只是停留在表面上,没有理解其 ...

  8. Java基础 之 System.getProperty()方法

    Java基础 之 System.getProperty()方法大全 public static void main(String[] args) { System.out.println(" ...

  9. java中equals和hashCode方法随笔二

    前几天看了篇关于java中equals和hashCode方法的解析 1.Object类中的equals方法和hashCode方法. Object类中的equals和hashCode方法简单明了,所有的 ...

  10. java基础知识点补充---二维数组

    #java基础知识点补充---二维数组 首先定义一个二维数组 int[][] ns={ {1,2,3,4}, {5,6,7,8}, {9,10,11,12}, {13,14,15,16} }; 实现遍 ...

随机推荐

  1. Resharper的使用

    一:Reshaper是什么 即便是那些整天攻击 .NET 和 C# 的人,也常常不得不承认 Visual Studio 确实是个够强大的 IDE,除非他认为更少的 IDE 功能和命令行调试才是更强大的 ...

  2. centos 7 安装 python-dev包提示No package python-dev available

    centos安装 python-dev包提示No package python-dev available: 出现此问题的原因是python-dev的包在centos的yum中不叫python-dev ...

  3. java支持跨平台获取cpuid、主板id、硬盘id、mac地址 (兼容windows、Linux)

    windows: package cn.net.comsys.helper.system.info;   import java.io.BufferedReader; import java.io.F ...

  4. C#虚函数和接口的区别

    接口只能声明不能实现,虚函数可以. 接口:对外提供可以访问的函数叫接口.虚函数不需要被强制重写,其本身含有实现部分. 抽象类:指派了派生类必须实现的函数(纯虚函数),不然编译不通过. 虚函数的限制:  ...

  5. Android系统学习小记

    序言 Android 应用的启动到一个页面显示出来,这个过程涉及到点击事件的处理,以及如何启动一个Activity,启动一个Activity之后,如何将Activity中我们的设置的ContentVi ...

  6. 11月13日上午ajax返回数据类型为JSON数据的处理

    ajax返回数据类型为JSON数据的处理 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" &qu ...

  7. 直接操作 SDL_Overlay YUV叠加上的像素

    根据这篇解码出yuv数据后 海思h264解码库 y,u,v数据全部存进数组内,          IntPtr y = _decodeFrame.pY;                 IntPtr ...

  8. margin双边距的问题

    margin:20px;height:20px;float:left margin:20px;height:20px;float:left

  9. Node+Express+node-mysql 实战于演习 全套mysql(增删改查)

    最近这段时间研究Node感觉不错,自己做了一个增删改查,虽然有些简陋,但是思想是想通的,其实所有项目都是增删改查,有助于初学者快速掌握Node 首先 本实例展示的是基于Node+Express+nod ...

  10. 转载:postgresql分区与优化

    --对于分区表constraint_exclusion 这个参数需要配置为partition或on postgres=# show constraint_exclusion ; constraint_ ...