概要

对于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. dt转换List CovertListHelper

    public class CovertListHelper { //传递过来的类型必须与数据库类型保持一致问题 public List<T> convertToList<T>( ...

  2. Numpy 基础

    Numpy 基础 参考https://www.jianshu.com/p/83c8ef18a1e8 import numpy as np 简单创建数组 # 创建简单列表 a = [1, 2, 3, 4 ...

  3. enum & json 之间的转换

    enum 转为 string:EnumMember & StringEnumConverter public enum CampaignStatus : Int32 { [EnumMember ...

  4. c#操作json的两种方式

    总结一下C#操作json的两种方式,都是将对象和json格式相转. 1.JavaScriptSerializer,继承自System.Web.Script.Serialization private ...

  5. .net 去除特殊字符

    str = Regex.Replace(str, @"<script[^>]*?>.*?</script>", "", Regex ...

  6. 【搬运工】redis 启动和关闭

    如果是用apt-get或者yum install安装的redis,可以直接通过下面的命令停止/启动/重启redis /etc/init.d/redis-server stop /etc/init.d/ ...

  7. IPTABLES使用总结(内网模拟银行网络)

    iptables中有以下三种类型的表: FILTER表,默认的表,包含以下三种内建链: INPUT链,发给本地sockets的包 FORWARD链,经由系统发送的包 OUTPUT链,本地生成并发出的包 ...

  8. vue--js里跳转页面

    我们知道在vue里进行页面跳转的话,我们使用<router-link>这个标签 那在构造函数里我们不能直接操纵DOM元素,我们又该如何进行页面跳转呢? 步骤1: 我们先在DOM里设置三个按 ...

  9. 【Redis】windows下redis服务的安装

    下载地址: https://github.com/MicrosoftArchive/redis/releases Redis 支持 32 位和 64 位.这个需要根据你系统平台的实际情况选择,这里我们 ...

  10. 实用的shell脚本面试题和答案

    1. 写一个shell脚本来得到当前的日期,时间,用户名和当前工作目录. 答案 : 输出用户名,当前日期和时间,以及当前工作目录的命令就是logname,date,who i am和pwd. 现在,创 ...