hashMap 数据结构

如上图所示,JDK7之前hashmap又叫散列链表:基于一个数组以及多个链表的实现,hash值冲突的时候,就将对应节点以链表的形式存储。

JDK8中,当同一个hash值(Table上元素)的链表节点数不小于8时,将不再以单链表的形式存储了,会被调整成一颗红黑树。这就是JDK7与JDK8中HashMap实现的最大区别。

其下基于 JDK1.7.0_80 JDK1.8.0_66 做的分析

JDK1.7中

使用一个Entry数组来存储数据,用key的hashcode取模来决定key会被放到数组里的位置,如果hashcode相同,或者hashcode取模后的结果相同(hash collision),那么这些key会被定位到Entry数组的同一个格子里,这些key会形成一个链表。

在hashcode特别差的情况下,比方说所有key的hashcode都相同,这个链表可能会很长,那么put/get操作都可能需要遍历这个链表

也就是说时间复杂度在最差情况下会退化到O(n)

JDK1.8中

使用一个Node数组来存储数据,但这个Node可能是链表结构,也可能是红黑树结构

如果插入的key的hashcode相同,那么这些key也会被定位到Node数组的同一个格子里。

如果同一个格子里的key不超过8个,使用链表结构存储。

如果超过了8个,那么会调用treeifyBin函数,将链表转换为红黑树。

那么即使hashcode完全相同,由于红黑树的特点,查找某个特定元素,也只需要O(log n)的开销

也就是说put/get的操作的时间复杂度最差只有O(log n)

听起来挺不错,但是真正想要利用JDK1.8的好处,有一个限制:

key的对象,必须正确的实现了Compare接口

如果没有实现Compare接口,或者实现得不正确(比方说所有Compare方法都返回0)

那JDK1.8的HashMap其实还是慢于JDK1.7的

简单的测试数据如下:

向HashMap中put/get 1w条hashcode相同的对象

JDK1.7:                                  put 0.26s,get 0.55s

JDK1.8(未实现Compare接口):put 0.92s,get 2.1s

但是如果正确的实现了Compare接口,那么JDK1.8中的HashMap的性能有巨大提升,这次put/get 100W条hashcode相同的对象

JDK1.8(正确实现Compare接口,):put/get大概开销都在320ms左右

为什么要这么操作呢?

我认为应该是为了避免Hash Collision DoS攻击

Java中String的hashcode函数的强度很弱,有心人可以很容易的构造出大量hashcode相同的String对象。

如果向服务器一次提交数万个hashcode相同的字符串参数,那么可以很容易的卡死JDK1.7版本的服务器。

但是String正确的实现了Compare接口,因此在JDK1.8版本的服务器上,Hash Collision DoS不会造成不可承受的开销。

参考资料:

jdk1.7.0_80的HashMap源码

jdk1.8.0_66的HashMap源码

Java 8系列之重新认识HashMap

HASH COLLISION DOS 问题

部分转载自:http://www.cnblogs.com/stevenczp/p/7028071.html

HashMap 在 Java1.7 与 1.8 中的区别的更多相关文章

  1. HashMap在Java1.7与1.8中的区别

    基于JDK1.7.0_80与JDK1.8.0_66做的分析 JDK1.7中 使用一个Entry数组来存储数据,用key的hashcode取模来决定key会被放到数组里的位置,如果hashcode相同, ...

  2. java中 HashMap和Hashtable,list、set和map 的区别

    摘自: http://blog.chinaunix.net/uid-7374279-id-2057584.html HashMap是Hashtable的轻量级实现(非线程安全的实现),他们都完成了Ma ...

  3. JDK1.7中HashMap死环问题及JDK1.8中对HashMap的优化源码详解

    一.JDK1.7中HashMap扩容死锁问题 我们首先来看一下JDK1.7中put方法的源码 我们打开addEntry方法如下,它会判断数组当前容量是否已经超过的阈值,例如假设当前的数组容量是16,加 ...

  4. 【转】HashMap、TreeMap、Hashtable、HashSet和ConcurrentHashMap区别

    转自:http://blog.csdn.net/paincupid/article/details/47746341 一.HashMap和TreeMap区别 1.HashMap是基于散列表实现的,时间 ...

  5. 集合 HashMap 的原理,与 Hashtable、ConcurrentHashMap 的区别

    一.HashMap 的原理 1.HashMap简介 简单来讲,HashMap底层是由数组+链表的形式实现,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的,如果定位到的数组位置不含链表 ...

  6. js中== 和===中的区别

    <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <m ...

  7. continue语句在for语句和while语句中的区别

    while语句的形式: while( expression ) statement for语句的形式: for( expression1; expression2;expression3 )   // ...

  8. Objective-C声明在头文件和实现文件中的区别

    Objective-C声明在头文件和实现文件中的区别 转自codecloud(有整理) 调试程序的时候,突然想到这个问题,百度一下发现有不少这方面的问答,粗略总结一下: 属性写在.h文件中和在.m文件 ...

  9. 在oracle中where 子句和having子句中的区别

    在oracle中where 子句和having子句中的区别 1.where 不能放在GROUP BY 后面 2.HAVING 是跟GROUP BY 连在一起用的,放在GROUP BY 后面,此时的作用 ...

随机推荐

  1. vue 隐藏滚动条

    element-ui隐藏组件scrollbar: <el-scrollbar style="height:100%"> </el-scrollbar> 真正 ...

  2. HTTP 错误 500.19 请求的页面的相关配置数据无效 解决办法

    "HTTP 错误 500.19 请求的页面的相关配置数据无效" 解决办法   HTTP 错误 500.19 - Internal Server Error无法访问请求的页面,因为该 ...

  3. Linux根目录解析

    根目录结构如下: 1. / - 根目录: 每一个文件和目录都从这里开始. 只有root用户具有该目录下的写权限.此目录和/root目录不同,/root目录是root用户的主目录. 2. /bin - ...

  4. ABOUT ME/OI回忆录

    \(ABOUT\ ME/OI回忆录\) 博主是一个退役的老菜鸡啦,学\(OI\)两年没搞过什么很厉害的东西,也没有做过很多题目,但是还是挺喜欢\(OI\)的. 在退役之后可能不会经常上博客园了,估计也 ...

  5. c# 判断一个数是不是质数或者求一个数的公约数的算法

    一个数是不是质数,就是判断一个数除了1和它本身还有没有其他的约数,如果有则是合数,否则是质数.其实本质都是求公约数. 求公约数是什么思路呢,就是找比它小的数不断尝试,能被整除则是其约数,否则继续尝试, ...

  6. SHA-1(安全哈希算法实现)

    如题,不知道sha-1的自己百度吧. #include <iostream> #include <vector> //定义vector数组 #include <strin ...

  7. java 去除末尾的零 如果小数点可以去除同时去除小数点

    String s; if(s.indexOf(".") > 0){ //正则表达 s = s.replaceAll("0+?$", "" ...

  8. 2017中国大学生程序设计竞赛 - 网络选拔赛 1004 HDU 6153 A Secret (字符串处理 KMP)

    题目链接 Problem Description Today is the birthday of SF,so VS gives two strings S1,S2 to SF as a presen ...

  9. vue项目的配置

    1.第一首先我们先安装:visual studio code开发环境,可以换成中文版的. 2.安装note.js   下载链接:https://nodejs.org/en/ 3.安装npm使用淘宝镜像 ...

  10. c# 生成随机N位数字串(每位可以重复)

    /// <summary> /// 生成随机数字窜 /// </summary> /// <param name="Digit">位数</ ...