HashMap源码详解与对比
前几天工作忙得焦头烂额时,同事问了一下关于Map的特性,刹那间懵了一下,紧接着就想起来了一些关于Map的一些知识,因为只要涉及到Collection集合类时,就会谈及Map类,因此理解好Map相关的知识是灰常重要的。
Collection集合类 与 Map类的层次结构,如下图:

Map是用来存储 Key-Value(K-V)键值对,且不允许Key重复。(JDK 1.8)
Map的子类包含Hashtable,HashMap,LinkedHashMap,TreeMap,EnumMap,J.U.C并发包下面包含ConcurrentHashMap,ConcurrentSkipListMap。
注:Hashtable是通过“synchronized”来实现同步,ConcurrentHashMap是通过“分段锁”来实现并发,ConcurrentSkipListMap是通过“跳表”来实现并发。
数据结构对比
JDK 1.7及之前,HashMap的底层是数组和链表结合使用,可以说其结构是单向链表构成的数组,即链表散列,又称拉链法,即数组中的每一个序列空间代表一链表,若在插入新元素时,如果哈希冲突,则将新的元素插入到冲突的序列空间的链表头部。

JDK 1.8之后,HashMap的底层是数组链表和二叉树结合使用,其结构是先以链表结构进行存储,如果当链表的节点数(阈值)大于等于8时,再将其转换为二叉树结构,若该二叉树结构的节点数大于64,则再次resize,触发rehash操作,重新分布节点。

JDK 1.7 | 1.8 HashMap 方法分析

1. hashmap在JDK1.7|1.8中hash()方法优化:此处参考博客链接_hashmap冲突的解决方法以及原理分析
其实关于hash优化以及为什么是2的次幂,能理解,但是总归没有辣么详细,所以就参考了其他博主的博客,如果不合适,还请提出,会进行删除的。

2. hashmap在JDK中resize()方法
当HashMap的长度超过临界值时(默认16*0.75=12),就会进行扩容。
通过调用resize()方法重新创建一个原HashMap大小的两倍的元素数组newTable,最大扩容到 MAXIMUM_CAPACITY = 1 << 30,并将原先table的元素全部移到newTable里面,重新计算hash,然后再重新根据hash分配位置。这个过程叫作rehash(最消耗性能的操作),因为它重新调用hash方法找到新的bucket位置。所以创建HashMap的时候,如果能预知元素个数,应尽量指定初始容量,这样可以提高HashMap的性能,避免重复resize()、rehash造成的性能损耗。
若在多线程情况下,多个线程同时出发resize()方法,会造成死循环操作

通过上面的解析,可以得到使用的是2次幂的扩展(指长度扩为原来2倍),所以,元素的位置要么是在原位置,要么是在原位置再移动2次幂的位置。
因此,我们在扩充HashMap的时候,不需要像JDK1.7的实现那样重新计算hash,通过使用(e.hash & oldCap)来计算高位和低位的hash值,来把原来在一个槽位上面的链表拆分成两个链表即可。
有一点注意区别,JDK1.7中rehash的时候,旧链表迁移新链表的时候,如果在新表的数组索引位置相同,则链表元素会倒置,但是从上图可以看出,JDK1.8不会倒置。
3. hashmap线程不安全,效率高
HashMap线程是不安全的,如果在多线程环境下,会出现未知异常(谨记)。
如果要在多线程情况下使用Map,有以下几种可供选择:
①、使用Hashtable(类中方法全是synchronized,效率特低,没有过这种);
②、使用Collections.synchronizedMap(new HashMap(...));实现,但不建议使用,使用迭代器遍历的时候修改映射结构容易出错;
③、使用guava中ImmutableMap,它是线程安全的,且方便快捷的组装键值对(建议);
ImmutableMap<String, Integer> map1 = ImmutableMap.<String, Integer>builder().put("A", 1) .put("B"2) .put("C", 3) .build();
ImmutableMap<String, Integer> map2 = ImmutableMap .<String, String>of("X", 0);
④、使用java.util.concurrent.ConcurrentHashMap,采用分段锁(JDK1.7) / CAS(JDK1.8)、相对安全,效率高(建议);
4. hashmap其他方法解析
其他方法后续有时间在进行补充...
JDK 中 HashMap 的其他问题
暂未想到,如有错误,还请指正 ...
HashMap源码详解与对比的更多相关文章
- Java HashMap源码详解
Java数据结构-HashMap 目录 Java数据结构-HashMap 1. HashMap 1.1 HashMap介绍 1.1.1 HashMap介绍 1.1.2 HashMap继承图 1.2 H ...
- HashMap源码详解(JDK7版本)
一.内部属性 内部属性源码: //内部数组的默认初始容量,作为hashmap的初始容量,是2的4次方,2的n次方的作用是减少hash冲突 static final int DEFAULT_INITIA ...
- Shiro 登录认证源码详解
Shiro 登录认证源码详解 Apache Shiro 是一个强大且灵活的 Java 开源安全框架,拥有登录认证.授权管理.企业级会话管理和加密等功能,相比 Spring Security 来说要更加 ...
- Activiti架构分析及源码详解
目录 Activiti架构分析及源码详解 引言 一.Activiti设计解析-架构&领域模型 1.1 架构 1.2 领域模型 二.Activiti设计解析-PVM执行树 2.1 核心理念 2. ...
- 源码详解系列(七) ------ 全面讲解logback的使用和源码
什么是logback logback 用于日志记录,可以将日志输出到控制台.文件.数据库和邮件等,相比其它所有的日志系统,logback 更快并且更小,包含了许多独特并且有用的特性. logback ...
- Mybatis源码详解系列(四)--你不知道的Mybatis用法和细节
简介 这是 Mybatis 系列博客的第四篇,我本来打算详细讲解 mybatis 的配置.映射器.动态 sql 等,但Mybatis官方中文文档对这部分内容的介绍已经足够详细了,有需要的可以直接参考. ...
- vue 源码详解(二): 组件生命周期初始化、事件系统初始化
vue 源码详解(二): 组件生命周期初始化.事件系统初始化 上一篇文章 生成 Vue 实例前的准备工作 讲解了实例化前的准备工作, 接下来我们继续看, 我们调用 new Vue() 的时候, 其内部 ...
- RocketMQ源码详解 | Producer篇 · 其二:消息组成、发送链路
概述 在上一节 RocketMQ源码详解 | Producer篇 · 其一:Start,然后 Send 一条消息 中,我们了解了 Producer 在发送消息的流程.这次我们再来具体下看消息的构成与其 ...
- RocketMQ源码详解 | Broker篇 · 其一:线程模型与接收链路
概述 在上一节 RocketMQ源码详解 | Producer篇 · 其二:消息组成.发送链路 中,我们终于将消息发送出了 Producer,在短暂的 tcp 握手后,很快它就会进入目的 Broker ...
随机推荐
- Total Commander的初次体验
从汉化新世纪下载到最新的TC张学思版后,运行文件只需依照其提示就可以完成该软件的安装.作为新手初次运行体验了以下功能: 一.目录跳转 1. 初次启动TC软件界面截图: 2. 按下Ctrl+d后,直接再 ...
- spring中使用缓存
一.启用对缓存的支持 Spring 对缓存的支持最简单的方式就是在方法上添加@Cacheable和@CacheEvict注解, 再添加注解之前,必须先启用spring对注解驱动的支持,基于java的配 ...
- max Count Group by
select UserName ,Max(LoginTime),count(1) from LoginLog group by UserName 只 group by UserName就行 可以得到L ...
- HDU6312 Game (多校第二场1004) 简单博弈
Game Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submis ...
- [转]SQL truncate 、delete与drop区别
转自: https://www.cnblogs.com/8765h/archive/2011/11/25/2374167.html 相同点: 1.truncate和不带where子句的delete.以 ...
- 「洛谷5017」「NOIP2018」摆渡车【DP,经典好题】
前言 在考场被这个题搞自闭了,那个时候自己是真的太菜了.qwq 现在水平稍微高了一点,就过来切一下这一道\(DP\)经典好题. 附加一个题目链接:[洛谷] 正文 虽然题目非常的简短,但是解法有很多. ...
- 【STL基础】deque
deque (double-ended queue) 构造函数: //default: deque<T> d; //空的vector //fill: deque<T> d(n) ...
- 【学习笔记】JavaScript的基础学习
[学习笔记]JavaScript的基础学习 一 变量 1 变量命名规则 Camel 标记法 首字母是小写的,接下来的字母都以大写字符开头.例如: var myTestValue = 0, mySeco ...
- Unix shell判断和比较
1. shell 的$! ,$?, $$,$@ $n $1 the first parameter,$2 the second... $# The number of c ...
- 9 Strings
1 Strings 1.1 Strings and GStrings Groovy允许你使用2种不同类型的字符串类型,分别是java.lang.String和groovy.lang.GS ...