HashMap原理

HashMap基本原理

HashMap原理图

HashMap主体上是由一个数组来存储数据,每一个索引的位置在这里我们可以先管他叫“桶”,假设你定一个HashMap的长度为16,那么就可以说是由16个桶构成的数组。当我们插入一个数据的时候,并不是按顺序一个一个向后存储的,在HashMap中专门定义了一套用于定位数据存储位置的哈希散列算法来确定数据具体的存储位置,因为是哈希散列计算,所以会存在哈希散列碰撞,意思就是相同的Key计算出的哈希值是相同的,在HashMap中使用的是拉链法来解决,散列碰撞问题。

当两个Key的计算出的Hash值相同,但是key的值不同,那么他们就会被定位到同一个HashMap的桶中,这个时候HashMap为了不让他们相互覆盖,就会在同一个桶中构建一个链表来存储他们。

但是随着HashMap中存储的数据越来越多,发生碰撞的概率就会越大,某个桶中的链表就会越来越长,当一个桶中的链表长度达到8这个阈值的时候,HashMap就将桶中的链表转换成红黑树,以达到提高性能的目的。

HashMap中的桶的长度并不会一成不变,而是当HashMap中存储元素的数量达到总容量的75%的时候就会,触发HashMap的扩容机制,这个时候HashMap就会对桶大小进行扩容,扩容的数量为当前桶大小的两倍。以达到提高性能的目的。

默认容量

HashMap的容量就是数组的长度,默认容量是16

  • 初始化时如果没有指定容量,则会使用默认值16,我们也可以为HashMap设置初始容量,而HashMap要求初始容量必须是2的幂次方。保证容量为2的幂次方的目的是:保证最后散列计算的结果是均匀
  • 在设置HashMap初始化容量时HashMap会对传入的初始容量值进行转换,并保证初始容量都能为2的幂次方(通过tableSizeFor来保证)

HashMap如何确定Key的唯一性

HashMap是不允许存在相同Key的。
key的唯一性同时满足如下两个条件:

  • 比较Key的Hash值是否相同。
  • 比较是否同一个对象或者key的值是否相等。
    以上两个条件都为true,则可认为是同一个Key。

HashMap为什么要求容量必须是2的幂次方

HashMap容量必须是 2 的幂次方,这样设计是为了保证散列计算更加均匀,计算索引的公式为 index = (n - 1) & hash

HashMap的扩容条件

  • HashMap中的键值对数据达到扩容阈值的时候就会触发扩容,扩容阈值=容量*负载因子,负载因子默认值为0.75。
  • HashMap扩容的时候,会创建一个容量为原来容量2倍的新数组,将数据从旧的数组中取出存入新的数组中

HashMap扩容负载因为为什么默认值设置为0.75

默认值是0.75是对HashMap查询插入时间和空间利用率权衡得出的结果,当负载因子为0.75的时候,避免了相当多的Hash冲突,HashMap底层的链表和红黑树的高度也比较低,在HashMap的空间利用率、插入和查询效率都比较高。

  • 如果设置负载因子值为1.0,就意味着当HashMap中的数组填满之后才会触发扩容,虽然空见的利用率达到了最高。但是也会因为Hash冲突增加,会导致底层的红黑树的高度增高,从而影响数据的插入和查询性能。
  • 如果设置负载因子值为0.5,就意味着当HahMap存储的数据达到容量的一半时就会触发扩容,虽然这样Hash冲突减少和查询效率会提升,但是在空间的利用率上就降低了,很多空间还没来得及使用就触发扩容了。

链表红黑树如何互相转换,阈值多少?

  • 链表转红黑树的阈值为:8
  • 红黑树转换成链表的阈值为:6

经过计算,在hash函数设计合理的情况下,发生hash碰撞8次的几率为百万分之6,从概率上讲,阈值为8足够用;至于为什么红黑树转回来链表的条件阈值是6而不是7或9?因为如果hash碰撞次数在8附近徘徊,可能会频繁发生链表和红黑树的互相转化操作,为了预防这种情况的发生。

插入元素的逻辑

  • 计算插入元素Key的Hash值
  • 判断HashMap的数组是否为空,为空则初始化数组,并返回数组长度,不为空直接返回数组长度
  • 确定数据插入位置,数据插入位置=Key的Hash值 & (HashMap数组长度-1)
  • 根据插入位置,取出旧元素,判断元素是否为空
  • 为空则初始化数据为链表头节点,然后插入数组
  • 如果数据不为空,判断新、旧元素的Key是否相等
  • 相等则将新插入的数据覆盖旧的数据,然后结束插入操作。
  • 不相等,则判断取出的数据是链表头节点还是红黑树头节点。
  • 如果是链表,则将新插入数据插入链表尾部,插入成功后判断链表长度是大于等于8,如果大于等于8则转换成红黑树。
  • 如果是红黑树,则将数据插入红黑树中。
  • 元素插入成功这之后,HashMap长度+1。
  • 判断HashMap的数据容量是否大于扩容阈值,如果大于则触发HashMap扩容。


    HashMap-PUT操作

HashMap线程不安全问题

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

推荐阅读更多精彩内容

  • 转载于:https://segmentfault.com/a/1190000012926722 1.概述 本篇文章...
    无色的叶阅读 732评论 2 7
  • 前文:HashMap是Java程序员最常用的映射(键值对)处理数据的容器。随着JDK版本的更新,1.8相较于1.7...
    是一动不动的friend阅读 1,190评论 0 1
  • 目录 构造器 构造器只是初始化负载因子,和扩容阈值。并没有初始化table。 阈值是比传入的大的最小2的幂次方(传...
    后来丶_a24d阅读 454评论 0 7
  • 散列表 定义:通过散列函数把元素的键值映射为下标,然后将数据存储在数组中对应下标的位置。当我们按照键值查询元素时,...
    回忆只能等候阅读 128评论 0 0
  • HashMap概述 Hash,又称散列。哈希表是一种以键-值(key-value) 存储数据的,和数组、链表、二叉...
    99793933e682阅读 552评论 0 6