概要

对于hashcode,相信很多朋友都不陌生,应为我们很多时候都需要用到这个,比如hashMap中就用到了,根据key的hash值来决定value存放的位置,之后来取得时候直接到指定的位置上那就行了,速度非常的快。今天我们就来看一下,hashcode在Java几个比较重要的类中具体是怎么用的?

什么是hashcode

要了解什么是hashcode,就得先了解什么哈希?

我们看下百度百科是怎么说的?

说白了,哈希就是一个函数,这个函数的实现可以说成是一种算法,通过这个算法我们得到一个值,就是hash值,这个值放哪儿呢?就得说另外一个东东,哈希表,hash值其实就是哈希表中的位置。

哈希算法有很多种的实现,比较常见的像 直接取模,平方取中等。

在Java中,每个对象都有hashcode,因为他们都继承于Object这个父类,它提供了一个native的返回对象哈希码值的方法,返回的是对象引用中存储的对象的内存地址(经过哈希之后在哈希表中的位置),并且有如下规定:

  1.在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是将对象进行 equals 比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另              一次执行,该整数无需保持一致。
  2.如果根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果。
  3.如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么对这两个对象中的任一对象上调用 hashCode 方法不 要求一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数结            果可以提高哈希表的性能。

第2点可以看出,如果重写了equals方法,那就需要重写hashcode方法,进而保证相等的对象得到的哈希值是一样的。这个还是很重要的,不然后面集合就没办法玩了。

第3点可以看出,两个对象不相等,但是哈希值却可能相等,这就是哈希碰撞。减少哈希碰撞,我们就要去不断的优化哈希算法,使得不同的对象通过它得到的hashcode是不一样的,也就是在哈希表中分布的比较均匀,而不是几种在某一块区域,这样产生碰撞的可能性就非常大。

下面我们就来看一下,Java几个比较重要的类是怎么来处理的?

String中的hashcode为什么使用31

string中的hashcode方法实现看起来很简单,代码如下:

    public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value; for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}

这个方法是被重写了的,因为string中的equals方法也是被重写了的。按照上面那个for循环,稍微推到下,就可以得出,最后得到的哈希值是这样计算的:

s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

这里面在计算的时候使用了31这个数,为什么呢?成千上万的数中,为什么偏偏就选中了它呢?真的只是因为多看了一眼?

当然不是,这个还是有根据的,首先,31是一个不大不小的指数,这个不大不小有什么好处呢?好处就在于它既可以保证散列的区间足够,又可以保证数据不会溢出丢失。另外一个,31*i这个通过数学手段的推导,可以得到这样一个结果:31*i=(i << 5) - i,这里面使用了位运算和减法来代替乘法,提高了性能。

hashmap中的hashcode

下面我们再来看一下hashcode在hashmap中的应用,我们先来看下jdk1.8中hashmap的hash方法:

     static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

这个方法有几个亮点:

1.如果key为空,则返回0

2.如果key不等于空,计算key的哈希值

3.将第二步里面计算得到的值无符号右移16位,高位补0

4.将第二步和第三步得到的结果进行异或运算(两个值相同则为0,相反则为1)

这里面前面两个步骤都比较好理解,按照正常情况,这时候就可以反回了啊,为什么还要进行后面的步骤呢?我们先来看下hashmap的put方法:

 if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
。。。。。。。以下省略。。。。。。

这里我们只要看上面标红的一行就行了,简单的说,你在向hashmap中存放元素的时候,这个元素放在哪边,就是这行代码决定的。我看下这行代码:就是用上面个hash方法返回的数和数组的长度减一之后做与运算,这里可能小伙伴又有疑问了啊,为啥子要进行与运算呢?这个跟我们将String类里面为什么使用31有异曲同工之妙,具体的推导过程我这边就不卖弄了,其实还是为了提升速度,这边这种其实就是最开始我们讲的直接取模 的哈希算法,只不过换了个样子,其实当 lenth = 2n 时,X % length = X & (length - 1),注意这里面的前提条件是不能丢的,少了就不对了,同学们是不是又突然想起来什么呢?

     /**
* The default initial capacity - MUST be a power of two.
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

看一下hashmap中对默认容量的定义中有这么一句话,必须是2的次方,说到底,就是为了去配合上面那个公式,成就一个完美的哈希算法。知道了后面的这个步骤,我们再来看上面那个无符号右移16位的问题。

假设存在这么一种情况:

对象 A 的 hashCode 为 1000010001110001000001111000000,对象 B 的 hashCode 为 0111011100111000101000010100000,数组的长度是16,运用上面那个公式计算的时候发现得到的结果都是0,冲突了,这是不行的,但是如果我们将hashcode右移16位,在进行异或运算,其实对于高位而言,是没有什么影响的,但是地位就会被打乱,其实真正参与的还是地位的数字,这样的得到的结果就会好很多,这样的设计是不是很厉害,既考虑到了效率,有能解决冲突的问题。

漫谈hashcode的更多相关文章

  1. 深入探究Java中hashCode()和equals()的关系

    目录 一.基础:hashCode() 和 equals() 简介 equals() hashCode() 二. 漫谈:初识 hashCode() 与 equals() 之间的关系 三. 解密:深入理解 ...

  2. 【道德经】漫谈实体、对象、DTO及AutoMapper的使用

    写在前面 实体和值对象 实体和对象 故常无欲以观其妙,常有欲以观其徼 初始实体和演化实体 代码中的DTO AutoMapper实体转换 后记 实体(Entity).对象(Object).DTO(Dat ...

  3. CSS实现水平|垂直居中漫谈

    利用CSS进行元素的水平居中,比较简单,手到擒来:行级元素设置其父元素的text-align center,块级元素设置其本身的left 和 right margins为auto即可.而撸起垂直居中, ...

  4. Java Map hashCode深究

    [Java心得总结七]Java容器下——Map 在自己总结的这篇文章中有提到hashCode,但是没有细究,今天细究整理一下hashCode相关问题 1.hashCode与equals 首先我们都知道 ...

  5. How to implement equals() and hashCode() methods in Java[reproduced]

    Part I:equals() (javadoc) must define an equivalence relation (it must be reflexive, symmetric, and ...

  6. 【转】漫谈iOS程序的证书和签名机制

    转自:漫谈iOS程序的证书和签名机制 接触iOS开发半年,曾经也被这个主题坑的摸不着头脑,也在淘宝上买过企业证书签名这些服务,有大神都做了一个全自动的发布打包(不过此大神现在不卖企业证书了),甚是羡慕 ...

  7. ArrayList_HashSet的比较及Hashcode分析

    ArrayList_HashSet的比较及Hashcode分析 hashCode()方法的作用   public static void main(String[] args) { Collectio ...

  8. UP board 漫谈(1)——从Atom到UP Board

    title: UP board 漫谈(1)--从Atom到UP Board date: 2016-12-26 12:33:03 tags: UP board categories: 开发板 perma ...

  9. OC与c混编实现Java的String的hashcode()函数

    首先,我不愿意大家需要用到这篇文章里的代码,因为基本上你就是被坑了. 起因:我被Java后台人员坑了一把,他们要对请求的参数增加一个额外的字段,字段的用途是来校验其余的参数是否再传递过程中被篡改或因为 ...

随机推荐

  1. Python3 tkinter基础 Tk quit 点击按钮退出窗体

             Python : 3.7.0          OS : Ubuntu 18.04.1 LTS         IDE : PyCharm 2018.2.4       Conda ...

  2. WinForm 绑定到嵌套对象上的属性

    WinFrom 绑定到嵌套对象上的属性 关键字: Windows Forms, DataBindings, Nested Class, 嵌套类 在 WinForm 中很早就已经支持数据绑定, 使用数据 ...

  3. Java 问题定位工具 -- jps

    概览 最近老大布置的任务就是质量加固,偶然看到了一些对于 Java 性能分析的介绍,因此,有了此篇学习笔记. JDK本身提供了很多方便的JVM性能调优监控工具,除了集成式的VisualVM和jCons ...

  4. loadrunner转码两种方式

    1.//使用转换函数将resp值做编码转换并存入msg lr_convert_string_encoding(“佛挡杀佛”),"utf-8",NULL,"msg" ...

  5. GhostCore核心使用完全指南 - 传送门

    Ghostcore,小坏制作,QQ 532073265,切记不要使用破解版本,后果自负 更新日志 数据表集合 了解模板机制 基本设置 自动备份数据库 自定义字符 扩展的GM命令 NPC脚本(包括幻化. ...

  6. Windows安装zookeeper 单机版

    首先需要安装JdK,从Oracle的Java网站下载,安装很简单,就不再详述. 1.下载zookeeper, https://mirrors.tuna.tsinghua.edu.cn/apache/z ...

  7. erlang的热更新

    erlang作为一个为电信级别而出现的语言,热更新是其最重要的特性之一  热代码升级-Erlang允许程序代码在运行系统中被修改.旧代码能被逐步淘汰而后被新代码替换.在此过渡期间,新旧代码是共存的. ...

  8. cookie,session,fileter,liscen

    会话技术: 会话:一次会话中发生多次请求和响应 一次会话:从浏览器的打开到关闭 功能:在会话的过程中 ,可以共享数据 cookie:客户端的会话技术session:服务端的会话技术 Cookie:小饼 ...

  9. vue.js+webpack在一个简单实例中的使用过程demo

    这里主要记录vue.js+webpack在一个简单实例中的使用过程 说明:本次搭建基于Win 7平台 Node.js 安装官网提供了支持多种平台的的LTS版本下载,我们根据需要来进行下载安装.对于Wi ...

  10. 还能不能愉快地起一个web服务啦?——1st Step!白话http和代码交互的那点儿事儿~

    学写python的时候,我们多多少少都接触到了web程序,然而你有没有想过,当浏览器发送了一个http请求时,等待接收这个请求的后端代码是一种什么样的思想感情? 就像下面这张图里画的一样,后端也许是一 ...