源码分析(一) HashMap 源码分析|JDK8
HashMap是一个普遍应用于各大JAVA平台的最最最常用的数据结构。<K,V>的存储形式使HashMap备受广大java程序员的喜欢。JDK8中HashMap发生了很大的变化,例如:之前在JDK7中存在的indexFor方法被移除了,哈希碰撞后不再单纯的使用链表来解决,而是使用红黑树+单向链表来解决,之前Entry<K,V>也改换为Node<K,V>。对于红黑树我觉得这个是稍微有些复杂的,包括插入和删除,红黑树的旋转和5个基本原则我觉得这个是需要花时间来慢慢整理的。对数据结构的理解一定是关键中的关键。使用红黑二叉树的主要目的就是提升了检索的效率,O(N)->O(logN)
下面就我自己对HashMap的理解做一些总结。以下代码均来自jdk 1.8.0_73
- 基础部分
(1)组成结构:通俗的说,HashMap就是结合了哈希表和链表的一种实现,在JDK8之前,之所以采用链表的目的其实是在于方便解决哈希碰撞带来的影响,提高hash值相同的插入速度。但是当一个桶转化为一个链表的时候,效率还是会有所下降。在JDK8之后采用链表+红黑树的方式来解决哈希碰撞带来的问题HashMap底层维护一个数组,数组中的每一项都是一个Node<K,V>。
- transient Node<K,V>[] table;//哈希表
- 3 transient Set<Map.Entry<K,V>> entrySet;//缓存
我们向 HashMap中所存储的数据实际上是存储在该数组当中,而HashMap中的key,value则是以Node<K,V>的形式存放在数组中。而这个Node应该放在数组的哪一个位置上(这个位置通常称为位桶或者hash桶,即hash值相同的Entry会放在同一位置,用链表相连或是红黑树),是通过key的hashCode来计算的。
- static final int hash(Object key) {
- int h;
- return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
- }
Map的put方法:其实看起来还是有点麻烦的,源码上put方法上的注释提示了,如果key值相同则会替换。
- /**
- *
- *
- * 将给定的value映射到Map上,如果该map上存在相同的key值,则会替换为现有的值
- *
- * @param key key with which the specified value is to be associated
- * @param value value to be associated with the specified key
- * @return the previous value associated with <tt>key</tt>, or
- * <tt>null</tt> if there was no mapping for <tt>key</tt>.
- * (A <tt>null</tt> return can also indicate that the map
- * previously associated <tt>null</tt> with <tt>key</tt>.)
- */
- public V put(K key, V value) {
- return putVal(hash(key), key, value, false, true);
- }
putVal可以划分为这样的一个流程:
1)当前哈希表是否为空,如果为空就初始化一个
2)然后插入这个value,要是这个数组没有就插入,有的话就替代原先的value
3)要是hash碰撞了,看看当前位置是红黑树还是链表,然后插入。同样,要是发现key相同则进行覆盖。
4)要是当前链表长度超过阈值了(默认是8),就转红黑树
这里要有个约定:
1) 哈希表是一个大的数组,类型是Node<K,V>,规定这个是每个桶,
2)桶里装的是bin
3)size是node的数量,也就是你实际存放K,V的数量
4)capacity是桶的数量,没规定初始化容量时,一般默认是16个
- final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
- boolean evict) {
- Node<K,V>[] tab; Node<K,V> p; int n, i;
- if ((tab = table) == null || (n = tab.length) == 0)
- n = (tab = resize()).length;
- if ((p = tab[i = (n - 1) & hash]) == null)
- tab[i] = newNode(hash, key, value, null);
- else {
- Node<K,V> e; K k;
- if (p.hash == hash &&
- ((k = p.key) == key || (key != null && key.equals(k))))
- e = p;
- else if (p instanceof TreeNode)
- e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
- else {
- for (int binCount = 0; ; ++binCount) {
- 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);
- return oldValue;
- }
- }
- ++modCount;
- if (++size > threshold)
- resize();
- afterNodeInsertion(evict);
- return null;
- }
源码分析(一) HashMap 源码分析|JDK8的更多相关文章
- JDK源码分析(三)——HashMap 下(基于JDK8)
目录 概述 内部字段及构造方法 哈希值与索引计算 存储元素 扩容 删除元素 查找元素 总结 概述 在上文我们基于JDK7分析了HashMap的实现源码,介绍了HashMap的加载因子loadFac ...
- [java源码解析]对HashMap源码的分析(二)
上文我们讲了HashMap那骚骚的逻辑结构,这一篇我们来吹吹它的实现思想,也就是算法层面.有兴趣看下或者回顾上一篇HashMap逻辑层面的,可以看下HashMap源码解析(一).使用了哈希表得“拉链法 ...
- 转:【Java集合源码剖析】HashMap源码剖析
转载请注明出处:http://blog.csdn.net/ns_code/article/details/36034955 您好,我正在参加CSDN博文大赛,如果您喜欢我的文章,希望您能帮我投一票 ...
- 源码解析之HashMap源码
关于HashMap的源码分析,网上已经有很多写的非常好的文章了,虽然多是基于java1.8版本以下的.Java1.8版本的HashMap源码做了些改进,理解起来更复杂点,但也不脱离其桶+链表或树的重心 ...
- 【Java集合源码剖析】HashMap源码剖析
转载出处:http://blog.csdn.net/ns_code/article/details/36034955 HashMap简介 HashMap是基于哈希表实现的,每一个元素是一个key-va ...
- 【源码分析】HashMap源码再读-基于Java8
最近工作不是太忙,准备再读读一些源码,想来想去,还是先从JDK的源码读起吧,毕竟很久不去读了,很多东西都生疏了.当然,还是先从炙手可热的HashMap,每次读都会有一些收获.当然,JDK8对HashM ...
- [java源码解析]对HashMap源码的分析(一)
最近有空的时候研究了下HashMap的源码,平时我用HashMap主要拿来当业务数据整理后的容器,一直觉得它比较灵活和好用, 这样 的便利性跟它的组成结构有很大的关系. 直接开门见山,先简要说明一下H ...
- 【jdk源码3】HashMap源码学习
可以毫不夸张的说,HashMap是容器类中用的最频繁的一个,而Java也对它进行优化,在jdk1.7及以前,当将相同Hash值的对象以key的身份放到HashMap中,HashMap的性能将由O(1) ...
- HashMap源码阅读与解析
目录结构 导入语 HashMap构造方法 put()方法解析 addEntry()方法解析 get()方法解析 remove()解析 HashMap如何进行遍历 一.导入语 HashMap是我们最常见 ...
- Java 源码刨析 - HashMap 底层实现原理是什么?JDK8 做了哪些优化?
[基本结构] 在 JDK 1.7 中 HashMap 是以数组加链表的形式组成的: JDK 1.8 之后新增了红黑树的组成结构,当链表大于 8 并且容量大于 64 时,链表结构会转换成红黑树结构,它的 ...
随机推荐
- eclipse使用maven install 命令,生成war包中没有jsp/js/css的解决方法
在pom.xml文件中添加如下11行代码就可以了. <build> <plugins> <plugin> <groupId>org.apache.mav ...
- windows/Linux主机重启NetBackup服务和后台进程
关闭并启动所有NetBackup服务和后台进程 要关闭并启动所有NetBackup服务和后台驻留程序,请从命令行输入以下命令: 在Windows上: 要关闭所有NetBackup服务: install ...
- python中的for循环如何控制步长
for i in range(开始/左边界, 结束/右边界, 步长): print i 例如 for i in range(1, 10, 2): print i 等价于 for (i=1;i<= ...
- nginx 配置路由规则转发配置记录
工作中公司要求针对经销商PC端和工厂PC端的访问地址固定访问. 经销商PC端 http://localhost/ 工厂PC端 http://localhost/fac 文件磁盘路径: /crm/n ...
- CentOS 7与 Windows双系统丢失Windows启动项及默认启动项修改
1.Windows启动项消失的原因: 在安装Win7.8/10系统+CentOS7双系统后,默认会将mbr(Main Boot Record)改写为grub2,默认的CentOS7无法识别Wind ...
- Python 2.x 和 3.x的区别
Python有两个版本,2.x 和 3.x ,两个版本不兼容,3.x 不不考虑对2.x代码的向后兼容. 在3.x中,一些语法,内建函数和对象的行为都有所调整. 大部分的python库都支持 pytho ...
- js动画效果
js能够按照预定的时间间隔重复调用一个函数,而这意味着我们可以随着时间的推移而不断改变某个元素的样式.动画是样式随时间变化的完美例子之一. js函数setTimeout能够让某个函数在经过一段预定的时 ...
- ssh框架复习
1.Hibernate中实体类的创建规则是什么? 2.hibernate中实体类的三种状态? 三种状态: 1. new 出来一个新对象 TakeTime takeTime = new TakeTime ...
- Hibernate进阶学习4
Hibernate进阶学习4 深入学习hibernate的查询语句 测试HQL查询 package com.hibernate.test; import com.hibernate.domain.Cu ...
- LNMP+HAProxy+Keepalived负载均衡 - LNMP基础环境准备
环境版本说明: 服务器系统:CentOS 7.5: ``` cat /etc/redhat-release CentOS Linux release 7.5.1804 (Core) # 输出结果 `` ...