Java数据结构_哈希表_基本概念

本文目标

哈希表的基本概念,哈希冲突,哈希函数

什么是哈希表

哈希表也叫做散列表(hash有剁碎的意思)
哈希表是空间换时间的典型应用
哈希表内部的数组元素,很多地方也叫做Bucket(桶),整个数组叫Buckets或者 Bucket Array

哈希冲突

哈希冲突也叫做哈希碰撞
2个不同的key,经过哈希函数计算得到相同的结果

JDK1.8的哈希冲突解决方案

默认采用采用单向链表将元素穿起来
在添加元素的时候也可以能由单向链表转成红黑树,比如当哈希表容量≥64 且单向链表的节点数量>8
当红黑树节点数量少到一定程度时候,又会转为单向链表

为什么使用单链表?

1.每次都是从头结点开始遍历
2.单向链表比双向链表少一个指针,节省内存空间

哈希函数

哈希表中哈希函数的实现步骤大概如下
1.先生成key的哈希值(必须是整数)
2.在让key的哈希值和数组的大小进行相关运算,生成一个索引值

public int  hash(Object key) {
    return hashCode(key) % table.length;
}

为了提高效率,可以使用&位运算取代%运算【前提:将数组的长度设计为2的幂(2^n)】

public int  hash(Object key) {
    return hashCode(key) & (table.length-1);
}

为什么可以使用&位运算取代%运算
二进制 2^n

10                 2^1
100                2^2
1000               2^3
10000              2^4

二进制 2^n - 1

01                 2^1-1
011                2^2-1
0111               2^3-1
01111              2^4-1

不难发现,都是2^n - 1 转成二进制都是一排1(最前面的0可以忽略)

假如说现在hash_code(key)为10101,然后与数组长度为(2^3 - 1)做&运算

  10101    哈希值
& 00111    数组的长度
---------------
  00101    索引值

得出来的结果 00101必然是小于00111的(数组的长度2^3 - 1)

良好的哈希函数
让哈希值更加均匀分布->减少哈希冲突此时->提升哈希表的性能

如何生成key的哈希值

key的常见种类可能有
1.整数,浮点数,字符串,自定义对象
2.不同种类的key,哈希值的生成方式不一样,但目标是一致的
尽量让每个key的哈希值是唯一的
进来让key的所有信息参与运算

1.整数的哈希值

把整数值当做哈希值
比如10的哈希值就是10

public static int  hashCode(int value) {
    return value;
}

2.浮点数的哈希值

将存储的二进制个数转为整数值

public static int  hashCode(float value) {
    return Float.floatToIntBits(value);
}

3.Long的哈希值

public static int  hashCode(long value) {
    return (int)(value ^ (value >>> 32));
}

4.Doble的哈希值

public static int  hashCode(double value) {
      //long类型的64位的二进制数据
     long bits = Double.doubleToLongBits(value);
     //进行低32位和高32位的 异或运算(相同为0,不同为1),算出哈希值
     return (int)(bits ^ (bits >>> 32));
}

>>> 和 ^ 的作用是?

1.高32bit和低32bit混合计算出32bit的哈希值
2.充分利用所有信息计算出哈希值


上图,value 和 value>>>32 (右移32位) 做异或^运算
此时,是value的低32位和高32位做异或^运算

|或运算,有一个为1就为1
所以不能用或运算,只能用异或
异或运算,相同为0,不同为1

5.字符串的哈希值

整数5489是如何计算出来的?

5 * 10^3 + 4 * 10^2 + 8 * 10^1 + 9 * 10^0

字符串是由若干个字符组成的
1.比如字符串jack,由j,a,c,k四个字符组成(字符的本质就是一个整数)
2.因此,jack的哈希值可以表示为

j * n^3 + a * n^2 + c * n^1 + k * n^0

等价于

[(j * n + a) * n + c] * n + k

3.在JDK中,乘数n为31,为什么使用31?
31是一个奇素数,JVM会将31 * i 优化成 (i<<5) -1

public static int  hashCode(String value) {
        int hashCode = 0;
        int len = value.length();
        for(int i=0;i<len;i++) {
            char c = value.charAt(i);
            hashCode = 31 * hashCode +c;
        }
        return  hashCode;
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容