JDK HashMap详解

HashMap的继承关系如图:


HashMap的继承关系图

初始空间大小 1<<4(16)
最大空间大小1 << 30 (1073741824)
哈希桶元素超过1,链接方式默认为链表形式,数量超过TREEIFY_THRESHOLD的阈值(默认8)将被转换成红黑树
哈希桶的元素如果降低到UNTREEIFY_THRESHOLD的阈值(默认6)会被重新转换成链表形式
最小的哈希表容量为64(4倍TREEIFY_THRESHOLD值)
哈希表节点的类型为Node<K,V>(实现Map.Entry<K,V> 接口)

结构为:
hash:哈希值
key:key
value:值
next:持有下一个节点的索引

put过程(以下是伪代码,贴有部分jdk8的代码实现):

  1. 计算key的哈希值
    采用哈希算法

     int h;
     (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16)
    

    这里也是哈希能兼容key为null的原因

    哈希算法为什么这样写?
    右位移16位,正好是32bit的一半,自己的高半区和低半区做异或,就是为了混合原始哈希码的高位和低位,以此来加大低位的随机性。而且混合后的低位掺杂了高位的部分特征,这样高位的信息也被变相保留下来。

  2. 将hash,key,value移交给putVal方法处理

  3. 处理节点数据

    1. 哈希表是空的:
      初始化哈希表,在初始化哈希表的过程中会进行扩容操作(resize()方法)会创建一个数组

      注意:实质上初始化数组和数组容量达到当前数组上限都会调用resize()方法并返回一个新的数组,达到容量上限的时候,会将原有数组的值copy(遍历到新的数组中)并返回新数组的引用,在扩容的时候会将原有哈希表中的元素分散到新的哈希表中,因为无论如何计算哈西元素的时候当前元素要么在当前位置(j)要么在(j+oldCap)位置,形成新的哈希桶,这样可以快速确定元素位置,而不用去重新确定元素对于所有桶的位置,这点很重要

    2. 根据当前key的哈希和当前的数组长度计算数值将被赋予的哈希桶位置,并赋值给如果当前哈希桶首元素为null,则创建一个新的node并赋予当前哈希表位置

    3. 如果不是null,则分为三种情况

      • map中已经有当前元素的key了.
        判定条件为:
        p.hash == hash &&
        ((k = p.key) == key || (key != null && key.equals(k)))
      • 当前节点是一个TreeNode节点(超过了树化阈值)
      • 没有当前元素并且不达到树化条件,则添加元素到链表中并将引用存入node节点的next中,如果添加元素之后达到树化条件,则将链表实现更新为红黑树的实现

      以下摘自网络
      但是超过这个阈值后HashMap开始将列表升级成一个红黑树,使用哈希值作为树的分支变量,如果两个哈希值不等,但指向同一个桶的话,较大的那个会插入到右子树里。如果哈希值相等,HashMap希望key值最好是实现了Comparable接口的,这样它可以按照顺序来进行插入。这对HashMap的key来说并不是必须的,不过如果实现了当然最好。如果没有实现这个接口,在出现严重的哈希碰撞的时候,你就并别指望能获得性能提升了。

    4. 如果map中存在次元素则替换元素的值并提供拓展点afterNodeAccess(Node<K,V> p)来处理这类数据

    5. 判定当前容量已经达到最大设置容量,如果达到给数组扩容

    6. 提供拓展点给子类提供添加之后的一些处理afterNodeInsertion(boolean evict)

get过程(以下是伪代码,贴有部分jdk8的代码实现):

  1. 链表查找:计算key的哈希值,并获取当前哈希桶,判定当前哈希桶中的首元素是不是需要的数据(进行equals)比较,如果不是,则依次遍历节点后驱直至找到key对应的node

  2. 遍历红黑树:判定首节点的类型是否是TreeNode,如果是从红黑树中获取元素并返回

  3. 如果没有查找到元素则返回null

     需要注意的是:
     containsKey()和containsValue()方法,其实质还是遍历一遍哈希桶,所以,如果只是判定在map中含有这个数据,用这个方法当然更好,但是如果需要这个数据做下一步运算就用get判空效率更高,因为containsKey()之后再次get相当于调用了两次遍历,效率相对下降许多
    

keySet()和values()方法:

keySet()和values()是因为hashMap继承自AbstractMap,它提供了超类实现将node中的key和value拆分到两个set中(keySet和values()),在第一次调用的时候(new KeySet的时候和values()的时候会创建一个对象,这个对象引用了key序列和value序列,这里并不需要去维护这个数组,只需要实现了set的迭代器name这个对象就会被索引到set里面(很神奇,我也惊讶了好久 但是实验过后的确是这样的)),之后就能取得set里面的数据,但是这个set的数值发生改变,map本身的数据也会发生改变,里面存放的只是对象的key/value引用。

ps:没有人比我写的更详细了吧,就问还有谁还有谁!!!

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