转:Hashmap在JDK8中的提升

HashMap使用key的hashCode()和equals()方法来将值划分到不同的桶里。桶的数量通常要比map中的记录的数量要稍大,这样 每个桶包括的值会比较少(最好是一个)。当通过key进行查找时,我们可以在常数时间内迅速定位到某个桶(使用hashCode()对桶的数量进行取模) 以及要找的对象。

这些东西你应该都已经知道了。你可能还知道哈希碰撞会对hashMap的性能带来灾难性的影响。如果多个hashCode()的值落到同一个桶内的 时候,这些值是存储到一个链表中的。最坏的情况下,所有的key都映射到同一个桶中,这样hashmap就退化成了一个链表——查找时间从O(1)到 O(n)。

当然这是在jdk8以前,JDK1.6中HashMap采用的是位桶+链表的方式,即我们常说的散列链表的方式,而JDK1.8中采用的是位桶+链表/红黑树的方式,也是非线程安全的。当某个位桶的链表的长度达到某个阀值的时候,这个链表就将转换成红黑树。

看下面的代码

[java]view plaincopy

//链表节点

staticclassNodeimplementsMap.Entry {

finalinthash;

finalK key;

V value;

Node next;

//省略

}

//红黑树节点

staticfinalclassTreeNodeextendsLinkedHashMap.Entry {

TreeNode parent;// red-black tree links

TreeNode left;

TreeNode right;

TreeNode prev;// needed to unlink next upon deletion

booleanred;

TreeNode(inthash, K key, V val, Node next) {

super(hash, key, val, next);

}

//省略

}

// HashMap的主要属性

publicclassHashMapextendsAbstractMap

implementsMap, Cloneable, Serializable {

// 槽数组,Node类型,TreeNode extends LinkedHashMap.Entry,所以可以存放TreeNode来实现Tree bins

transientNode[] table;

transientSet> entrySet;

transientintsize;

// 去掉了volatile的修饰符

transientintmodCount;

intthreshold;

finalfloatloadFactor;

...

}

[java]view plaincopy

//计算key的hash

staticfinalinthash(Object key) {

inth;

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

}

饭后我们在看看具体的put和get方法

[java]view plaincopy

publicV get(Object key) {

Node e;

return(e = getNode(hash(key), key)) ==null?null: e.value;

}

finalNode getNode(inthash, Object key) {

Node[] tab;

Node first, e;

intn; K k;

//hash & length-1 定位数组下标

if((tab = table) !=null&& (n = tab.length) >0&&

(first = tab[(n -1) & hash]) !=null)

{

if(first.hash == hash &&// always check first node

((k = first.key) == key || (key !=null&& key.equals(k))))

returnfirst;

if((e = first.next) !=null) {

/*第一个节点是TreeNode,则采用位桶+红黑树结构,

* 调用TreeNode.getTreeNode(hash,key),

*遍历红黑树,得到节点的value

*/

if(firstinstanceofTreeNode)

return((TreeNode)first).getTreeNode(hash, key);

do{

if(e.hash == hash &&

((k = e.key) == key || (key !=null&& key.equals(k))))

returne;

}while((e = e.next) !=null);

}

}

returnnull;

}

finalTreeNode getTreeNode(inth, Object k) {

//找到红黑树的根节点并遍历红黑树

return((parent !=null) ? root() :this).find(h, k,null);

}

/*

*通过hash值的比较,递归的去遍历红黑树,这里要提的是compareableClassFor(Class k)这个函数的作用,在某些时候

*如果红黑树节点的元素are of the same "class C implements Comparable" type

*利用他们的compareTo()方法来比较大小,这里需要通过反射机制来check他们到底是不是属于同一个类,是不是具有可比较性.

*/

finalTreeNode find(inth, Object k, Class kc) {

TreeNode p =this;

do{

intph, dir; K pk;

TreeNode pl = p.left, pr = p.right, q;

if((ph = p.hash) > h)

p = pl;

elseif(ph < h)

p = pr;

elseif((pk = p.key) == k || (k !=null&& k.equals(pk)))

returnp;

elseif(pl ==null)

p = pr;

elseif(pr ==null)

p = pl;

elseif((kc !=null||

(kc = comparableClassFor(k)) !=null) &&

(dir = compareComparables(kc, k, pk)) !=0)

p = (dir <0) ? pl : pr;

elseif((q = pr.find(h, k, kc)) !=null)

returnq;

else

p = pl;

}while(p !=null);

returnnull;

}

[java]view plaincopy

//put(K key,V value)函数

publicV put(K key, V value) {

returnputVal(hash(key), key, value,false,true);

}

finalV putVal(inthash, K key, V value,booleanonlyIfAbsent,

booleanevict) {

Node[] tab;

Node p;

intn, i;

//如果table为空或者长度为0,则resize()

if((tab = table) ==null|| (n = tab.length) ==0)

n = (tab = resize()).length;

//找到key值对应的槽并且是第一个,直接加入

if((p = tab[i = (n -1) & hash]) ==null)

tab[i] = newNode(hash, key, value,null);

else{

Node e;

K k;

//第一个node的hash值即为要加入元素的hash

if(p.hash == hash &&

((k = p.key) == key || (key !=null&& key.equals(k)))){

e = p;

}elseif(pinstanceofTreeNode)//第一个节点是TreeNode,即tree-bin

/*Tree version of putVal.

*final TreeNode putTreeVal(HashMap map, Node[] tab,int h, K k, V v)

*/

e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);

else{

//不是TreeNode,即为链表,遍历链表

for(intbinCount =0; ; ++binCount) {

/*到达链表的尾端也没有找到key值相同的节点,

*则生成一个新的Node,并且判断链表的节点个数是不是到达转换成红黑树的上界

*达到,则转换成红黑树

*/

if((e = p.next) ==null) {

p.next = newNode(hash, key, value,null);

if(binCount >= TREEIFY_THRESHOLD -1)// -1 for 1st

treeifyBin(tab, hash);

break;

}

if(e.hash == hash &&

((k = e.key) == key || (key !=null&& key.equals(k))))

break;

p = e;

}

}

if(e !=null) {// existing mapping for key

V oldValue = e.value;

if(!onlyIfAbsent || oldValue ==null)

e.value = value;

afterNodeAccess(e);

//返回旧的value值

returnoldValue;

}

}

++modCount;

if(++size > threshold)

resize();

afterNodeInsertion(evict);

returnnull;

}

HashMap会动态的使用一个专门的treemap实现来替换掉它。这样做的结果会更好,是O(logn),而不是糟糕的O(n)。它是如何工作 的?前面产生冲突的那些KEY对应的记录只是简单的追加到一个链表后面,这些记录只能通过遍历来进行查找。但是超过这个阈值后HashMap开始将列表升 级成一个二叉树,使用哈希值作为树的分支变量,如果两个哈希值不等,但指向同一个桶的话,较大的那个会插入到右子树里

个性能提升有什么用处?比方说恶意的程序,如果它知道我们用的是哈希算法,它可能会发送大量的请求,导致产生严重的哈希碰撞。然后不停的访问这些 key就能显著的影响服务器的性能,这样就形成了一次拒绝服务攻击(DoS)。JDK 8中从O(n)到O(logn)的飞跃,可以有效地防止类似的攻击,同时也让HashMap性能的可预测性稍微增强了一些。

转载请注明出处http://blog.csdn.NET/a837199685

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

推荐阅读更多精彩内容

  • 一、基本数据类型 注释 单行注释:// 区域注释:/* */ 文档注释:/** */ 数值 对于byte类型而言...
    龙猫小爷阅读 4,254评论 0 16
  • 本文转自 https://zhuanlan.zhihu.com/p/21673805 美团点评技术团队· 3 个月...
    抓兔子的猫阅读 1,052评论 0 1
  • 早就听说小三峡风景秀美,在很多杂志书本上都看到过小三峡风景的图片,但从没身临其境感受过,无疑是一大遗憾,今天有幸能...
    杨小小M阅读 189评论 0 0
  • 星星美丽,因为那里有一朵看不见的花。 文章是通过是通过小王子在各个星球的旅行而写出的一些小故事,每一章就讲述一个故...
    14广告刘灿阅读 774评论 0 2
  • 那个时刻,好像长久逗号顿号后的句号 接下来的事情,就是另起一行(东东 赵子明著) 最能滋养人的,莫过于这些越品越美...
    帕帕尼尼阅读 171评论 0 0