HashMap 在 Java1.7 与 1.8 中的区别
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源码
部分转载自:http://www.cnblogs.com/stevenczp/p/7028071.html
HashMap 在 Java1.7 与 1.8 中的区别的更多相关文章
- HashMap在Java1.7与1.8中的区别
基于JDK1.7.0_80与JDK1.8.0_66做的分析 JDK1.7中 使用一个Entry数组来存储数据,用key的hashcode取模来决定key会被放到数组里的位置,如果hashcode相同, ...
- java中 HashMap和Hashtable,list、set和map 的区别
摘自: http://blog.chinaunix.net/uid-7374279-id-2057584.html HashMap是Hashtable的轻量级实现(非线程安全的实现),他们都完成了Ma ...
- JDK1.7中HashMap死环问题及JDK1.8中对HashMap的优化源码详解
一.JDK1.7中HashMap扩容死锁问题 我们首先来看一下JDK1.7中put方法的源码 我们打开addEntry方法如下,它会判断数组当前容量是否已经超过的阈值,例如假设当前的数组容量是16,加 ...
- 【转】HashMap、TreeMap、Hashtable、HashSet和ConcurrentHashMap区别
转自:http://blog.csdn.net/paincupid/article/details/47746341 一.HashMap和TreeMap区别 1.HashMap是基于散列表实现的,时间 ...
- 集合 HashMap 的原理,与 Hashtable、ConcurrentHashMap 的区别
一.HashMap 的原理 1.HashMap简介 简单来讲,HashMap底层是由数组+链表的形式实现,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的,如果定位到的数组位置不含链表 ...
- js中== 和===中的区别
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <m ...
- continue语句在for语句和while语句中的区别
while语句的形式: while( expression ) statement for语句的形式: for( expression1; expression2;expression3 ) // ...
- Objective-C声明在头文件和实现文件中的区别
Objective-C声明在头文件和实现文件中的区别 转自codecloud(有整理) 调试程序的时候,突然想到这个问题,百度一下发现有不少这方面的问答,粗略总结一下: 属性写在.h文件中和在.m文件 ...
- 在oracle中where 子句和having子句中的区别
在oracle中where 子句和having子句中的区别 1.where 不能放在GROUP BY 后面 2.HAVING 是跟GROUP BY 连在一起用的,放在GROUP BY 后面,此时的作用 ...
随机推荐
- Shell记录-Shell命令(磁盘)
inux中df命令的功能是用来检查linux服务器的文件系统的磁盘空间占用情况.可以利用该命令来获取硬盘被占用了多少空间,目前还剩下多少空间等信息. 1.命令格式 df [选项] [文件] Shell ...
- 机器学习算法整理(二)梯度下降求解逻辑回归 python实现
逻辑回归(Logistic regression) 以下均为自己看视频做的笔记,自用,侵删! 还参考了:http://www.ai-start.com/ml2014/ 用梯度下降求解逻辑回归 Logi ...
- IE10下 FormsAuthentication.SetAuthCookie无效的问
问题是这样的,我在本地测试设置身份验证票据都没问题,发布到服务器后访问地址添加了一些特殊的字符,看起来像加过密的,如下: http://www.example.com/(F(1xe9eXIxPzMAL ...
- c++程序设计中的函数重载
函数重载的意思是在一个作用域内(命名空间内)定义了某个或某些具有相同名称的函数,但是他们的参数列表和定义(实现)不相同,如果相同的话,就没啥意义了.当调用一个重载函数时,编译器会通过所使用的参数类型. ...
- javascript 中的类数组和数组
什么是类数组呢? 我们先来看一段代码: function fn() { console.dir(arguments); } fn(1,2,3,4,5,6,7,8,9,10); 这段代码的执行后,在 c ...
- 说说C语言运算符的“优先级”与“结合性”
论坛和博客上常常看到关于C语言中运算符的迷惑,甚至是错误的解读.这样的迷惑或解读大都发生在表达式中存在着较为复杂的副作用时.但从本质上看,仍然是概念理解上的偏差.本文试图通过对三个典型表达式的分析,集 ...
- Hibernate5笔记2--单表的增删改查操作
单表的增删改查操作: (1)定义获取Session和SessionFactory的工具类: package com.tongji.utils; import org.hibernate.Session ...
- 【Android开发】之MediaPlayer的错误分析
最近在做媒体播放器,使用了Android自带的MediaPlayer,经常性会碰到MediaPlayer报错的情况,找过网上的,感觉总结的不是很好或者比较散.下面,我来总结一下使用MediaPlaye ...
- Python开发环境(1):Eclipse+PyDev插件
电脑:小米笔记本电脑Pro 15.6寸(i5-8250U),操作系统:Windows 10,JDK版本:1.8.0_152(环境变量已配置) Step 1.下载Eclipse 根据我的CPU型号,选择 ...
- 如何在线预览github上的html页面?
可以通过http://htmlpreview.github.io/这个网站,直接在线预览html页面. ↓ ↓ 可以发现:这个网站直接将github上的页面地址当做参数来传递.