基于JDK1.7.0_80JDK1.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 问题

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

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

    hashMap 数据结构 如上图所示,JDK7之前hashmap又叫散列链表:基于一个数组以及多个链表的实现,hash值冲突的时候,就将对应节点以链表的形式存储. JDK8中,当同一个hash值(Ta ...

  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. laravel5.1 关联模型保存的方法(使用associate方法)

    模型定义 class User { public function customer() { return $this->hasOne('Customer'); } } class Custom ...

  2. SpringBoot项目中使用swagger2暴露resftul接口增加JWT来进行安全性验证

    首先推荐两篇文章: 关于保护RestAPI的一些介绍: http://www.jianshu.com/p/6307c89fe3fa token与session的一些区别漫谈: http://www.j ...

  3. libuv移植到ios

    libuv官网只提供了os x的编译方法,没有IOS的.既然os x和ios的系统内核差不多,并且编译工具都是xcode,那我们只要重新指定cpu架构,就可以编译出ios版的了. 1.安装python ...

  4. Vue.js基础 笔记

    Vue.js的声明: < script src = “https://unpkg.com/vue” ></ script > el:值可以是CSS选择符.HTML元素.或者是返 ...

  5. Python学习笔记(补充)Split 用法

    >>> u = "www.doiido.com.cn" #使用默认分隔符 >>> print u.split() ['www.doiido.co ...

  6. Flex用HTTPService调用servlet返回中文乱码解决

    servlet中使用URLEncoder.encode对输出内容进行编码 Flex中使用decodeURIComponent进行解码

  7. LintCode 510: Maximal Rectangle

    LintCode 510: Maximal Rectangle 题目描述 给你一个二维矩阵,权值为False和True,找到一个最大的矩形,使得里面的值全部为True,输出它的面积 Wed Nov 2 ...

  8. 原生js写Ajax

    //原生js写ajax就像打电话 //打电话分下面4步//1.拿出手机//2.拨号//3.说话//4.挺对方说话 //ajax也分下面4步//1.创建ajax对象//2.连接到服务器//3.发送请求( ...

  9. java规范之checkstyle

    1. 概述 随着中心的代码规范的建立和实施,项目组对代码规范要求,以及软件工程师们对自身代码的编写规范重要性的认知,“代码规范”已经成为了中心的一个“热词”.然后怎么才能写出有规范的代码,怎么才能养成 ...

  10. hdu 5319 Painter(杭电多校赛第三场)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5319 Painter Time Limit: 2000/1000 MS (Java/Others)   ...