Java - HashMap解析

在Java中,HashMap是一种很常用的数据结构,很多时候都可以见到它的身影,因为它的效率比较高,下面来看看它是个怎么样的存在。

1、首先,从构造函数讲起:

HashMap有4个构造函数,前面三个基本相同

无参构造函数设置默认装载因子(DEFAULT_LOAD_FACTOR),一个参数的为HashMap的大小(hashMap中数组的大小),此构造函数调用两个参数的构造函数,传数组大小和默认装载因子,有看到,这几个构造函数都么有初始化数组的大小,第四个构造函数的参数是一个map,就是遍历map,然后把值put到新建的map里面。

2、HashMap是由一个数组加链表(或者红黑树)组成的数据结构。它是怎么存数据的呢,一起看看put方法。

put方法中调用putVal

看看这些参数是什么含义:

hash:经过一次hash的key

key和value就是put方法的key和value了

onlyIfAbsent:为true的时候表示不替换,就是如果已经存在某个key了,那么这个key对应的值就不会被替换,即使再put一个相同的key,值也还是原来的值

evict:这个是在LinkedHashMap中使用的

下面看看这个函数具体做了什么,首先判断table是否为空,为空则调用resize方法初始化,前面说到在构造函数时并没有初始化数组,原来是留到put值的时候才初始化,这在一定程度上节省了内存,因为真正用到的时候才分配内存。接着就是再次hash,找到这次的key在数组中所对应的位置,如果该位置为空,则放在那个位置上就可以了,如果该位置不为空,则表明发生了冲突,HashMap的解决冲突的方法是首先用链表,把新加入的值存到链表的后面,如果链表的长度达到8以后,就自动变成红黑树(调用treeifyBin(tab, hash))。因为HashMap中存在可能存在链表和树两个结构,所以解决冲突之前,先判断数组当前位置后面的节点是树还是链表,树的话,直接add进去,链表的话就进行上面所说的操作。这个函数的最后还有一点,就是判断扩容

扩容在Java8之后变得很巧妙

resize()

前面的判断就不说了有兴趣自己研究研究,下面说一下精髓部分,首先是把数组后面的链表或者红黑树分成两个链表(这里选链表举例),一个是hash值和旧的数组长度(odlCap)按位与等于0的,另一个链表是不等于0的,为什么要这么划分呢,下面是见证奇迹的时刻:假设旧的数组长度为4(这里为了方便,就假设为4),那么oldCap变成二进制就是0100,这里和putVal方法中的二次hash不同,二次hash是和cap-1按位与,这里是cap,没有减1。

1)

如果结果为0,说明e.hash值的第三位(低位开始)为0,当扩容后长度变成8后,e.hash与8-1(00111)进行hash的结果和与4-1(00011)(原来数组长度-1) 进行hash的结果一样

所以扩容前后相对于数组的位置不变,于是有 newTab[j] = loHead;

2)

如果结果不为0,说明e.hash值的第三位(低位开始)为1,当扩容后长度变成8后,e.hash与8-1(00111)进行hash的结果相当于 与4-1(00011)(原来数组长度-1) 进行hash的结果再加oldCap(4)

所以有 newTab[j + oldCap] = hiHead,扩容后的位置等于原来的位置 + 原来的数组大小

HashMap中的数组长度是2的幂次方有这个好处,避免二次hash。java8后链表采用尾插入法,保持原来的顺序,避免了死循环,8之前采用头插入法,会出现死循环。

8之前采用头插入法,会出现死循环:假设本来有一个链表 a -> b ,扩容时线程1、线程2同时拿到b节点的指针,线程1挂起,此时,线程2开始执行,迁移节点b到新数组,链表变成 b -> a,此时线程1继续从节点b开始迁移到新数组,迁移节点b后,线程1中的链表变成 b -> a,因为由于线程2的复制,节点b的next指向a,所以线程1继续迁移b.next (即节点a),于是出现死循环 a -> b -> a

java8后增加红黑树,提升查找新能,改用尾插法。如果按照之前的扩容算法,采用尾插法的话,要么变量一遍链表,要么增加一个数组存尾指针,开销都不小,新的扩容算法只需要两个尾指针就搞定,从某种意见上讲,性能有所提升。

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

推荐阅读更多精彩内容

  • 注:本文是以Android中的HashMap进行讲解 问题: 1、HashMap采用的是什么数据结构?2、Hash...
    jxiang112阅读 723评论 0 1
  • 前言 上篇文章讲解了JDK1.7中的HashMap源码, 主要采用数组+链表来实现, 根据元素的hash计算出来的...
    海之韵Baby阅读 359评论 0 0
  • HashMap 可以算是 Java 中最常用的几个集合类之一。这一篇文章将在代码层面上详细解释 HashMap 的...
    王聪帅阅读 680评论 0 1
  • 一、概述 HashMap的底层数据结构是数组,但是数组中存放的并不是一个对象而是链表。所以也可以成HashMap的...
    史云龙阅读 422评论 0 0
  • 今年圣诞只是我平凡日程中的一天,和往常没有任何的不同。 早上上课,和小伙伴一起选自己想买的东西,结果知道下午,才算...
    Bri晨阅读 241评论 0 0