仔细瞄一下HashMap是怎么干活的
以下分析基于jdk11.0.2
1. 创建HashMap时发生了什么?
HashMap(),HashMap(int initialCapacity),HashMap(int initialCapacity, float loadFactor)。这三个方法都直接或间接地会初始化loadFactor(加载因子)和threshold(扩容阈值)。其中threshold=capacity*loadFactor。
1.1 threshold如何确定?
当调用HashMap()创建HashMap时,threshold的值会在第一次resize()时赋值。由DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY可知threshold=0.75*16=12
当调用HashMap(int initialCapacity)/HashMap(int initialCapacity, float loadFactor) 创建HashMap时,threshold由 loadFactor*tableSizeFor(int cap) 计算得出。
2. 调用put(K key, V value)时发生了什么?
int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
该方法首先调用了hash()方法获取key对应的hash值,然后调用putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict)…
2.1. hash(Object key)做了些什么?
该方法将key的hashCode的高16位与低16位进行了一次异或位运算(hashCode为32bit的int类型)。v1.8+中该方法的实现较之前版本更容易发生hash碰撞(之前版本为4次异或运算),这是权衡性能和红黑树的优化…
2.2. putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict)做了什么?
该方法除了供put()调用,也提供给putIfAbsent()调用。在此暂时讨论put()调用的情况,即 boolean onlyIfAbsent=false; boolean evict=true;
下面列出用无参构造函数new HashMap()创建的对象进行put的几种情况:
2.2.1. 第一次put时,执行步骤如下:
1. 执行resize(),将map中的table初始化为大小为DEFAULT_INITIAL_CAPACITY的Node数组;threshold赋值为DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY。
2. 使用hash, key, value创建Node节点,作为链表的头节点存于table[i]中,下标为 i = (n - 1) & hash 。
2.2.2. 当put后table[]内节点数<=threshold(默认threshold=12,而此时table[].size也就是capacity应为16,这两个值会随着resize更新)时,执行步骤如下:
1. 找到hash对应table[]中的链表/树
2. 当table[]存的是链表时,把key-value存入链表尾节点或替换key对应节点的value值,并判断链表长度是否>TREEIFY_THRESHOLD(默认值8),如果是则调用treeifyBin()。调用treeifyBin()时会判断是否需要将该链表转为树。当table[].size>=MIN_TREEIFY_CAPACITY会转为树,否则只是resize()扩容;而当table[]存的是树时,调用TreeNode.putTreeVal()在树中存入/替换。
2.2.3. 当put后table[]内节点数>threshold时:
执行完2.2.2的操作后,执行执行resize():capacity翻倍(<<1),threshold也重新计算。
画了张流程图用来精简表示putVal:
3. 调用resize()时发生了什么?
在putVal途中调用有两种情况下HashMap会调用resize()进行扩容和table[]数据迁移(迁移几率50%):
3.1. 第一次调用putVal后调用resize():
3.1.1. 未指定initialCapacity或loadFactor值:
创建table[],大小为DEFAULT_INITIAL_CAPACITY(默认值16);赋值threshold=DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY(默认值12)。
3.1.2. 已指定initialCapacity或loadFactor值:
创建容量为tableSizeFor(initialCapacity)的table[];给扩容阈值赋值 threshold = loadFactor * tableSizeFor(initialCapacity)。
简单说明一下tableSizeFor(int cap)函数:返回值为大于等于cap且与cap差值最小的2^n的值。例如3->4,4->4,9->16,65->128。
3.3. table[]内节点数>threshold时,执行步骤如下:
3.3.1. 重新计算table[]容量capacity和扩容阈值threshold,值皆为原值的2倍(<<1),创建新table[capacity]
3.3.2. 遍历原table[]中的链表/树,
当链表为单节点时:将该节点放至新table[],下标为hash&(capacity-1) ;
当链表为多节点时:遍历该链表并分离出一条需要移动位置的链表,将2条链表放至新table[]。可根据hash&oldCapacity==0判断Node是否需要移动;
当链表为红黑树时:调用TreeNode.split()将树拆分/移动。当树的大小<=UNTREEIFY_THRESHOLD(默认6)时会退化成链表。
仔细瞄一下HashMap是怎么干活的的更多相关文章
- HashMap 源码详细分析(JDK1.8)
一.概述 本篇文章我们来聊聊大家日常开发中常用的一个集合类 - HashMap.HashMap 最早出现在 JDK 1.2中,底层基于散列算法实现.HashMap 允许 null 键和 null 值, ...
- Hashmap误区
HashMap简介 HashMap 是一个散列表,它存储的内容是键值对(key-value)映射.HashMap 继承于AbstractMap,实现了Map.Cloneable.java.io.Ser ...
- centos7下Redis-Sentinel安装和配置
一.Redis的安装 1.从官网https://redis.io/download下载最新的stable版本(也可以下载unstable版本)redis-4.0.9.tar.gz. 2.上传到Cent ...
- NEXYS 3开发板练手--LED与数码管时钟
做科研的时候从学校拿到一块基于Xilinx公司Spartan-6主芯片的FPGA开发板,因为之前一直在用Altera公司的FPGA,一开始接触它还真有点不太习惯.但毕竟核心的东西还是不会变的,于是按照 ...
- JAVA源码分析-HashMap源码分析(一)
一直以来,HashMap就是Java面试过程中的常客,不管是刚毕业的,还是工作了好多年的同学,在Java面试过程中,经常会被问到HashMap相关的一些问题,而且每次面试都被问到一些自己平时没有注意的 ...
- HashMap源码阅读笔记(基于jdk1.8)
1.HashMap概述: HashMap是基于Map接口的一个非同步实现,此实现提供key-value形式的数据映射,支持null值. HashMap的常量和重要变量如下: DEFAULT_INITI ...
- [Java集合] 彻底搞懂HashMap,HashTable,ConcurrentHashMap之关联.
注: 今天看到的一篇讲hashMap,hashTable,concurrentHashMap很透彻的一篇文章, 感谢原作者的分享. 原文地址: http://blog.csdn.net/zhanger ...
- HashMap封装的数据用循环快速添加进list中产生的数据集全部相同的问题
一.问题概述 在一次使用SimpleAdapter时,Data需要使用传入一条数据(Image.Text),该数据条使用HashMap封装.在用HashMap封装的数据用循环快速添加进list中产生了 ...
- [转]不正当使用HashMap导致cpu 100%的问题追究
以前项目中遇到类似业务,但使用的是CurrentHashMap,看到这篇文章,转载记录,警示自己. 以下内容转自: 转载自并发编程网 – ifeve.com(http://ifeve.com/hash ...
随机推荐
- 一句话懂什么是JS闭包
无论何时声明新函数并将其赋值给变量,都要存储函数定义和闭包.闭包包含在函数创建时作用域中的所有变量,它类似于背包.函数定义附带一个小背包,它的包中存储了函数定义创建时作用域中的所有变量. 我将永远记住 ...
- 概括的描述一下Spring注册流程
Spring经过大神们的构思.编码,日积月累而来,所以,对其代码的理解也不是一朝一夕就能快速完成的.源码学习是枯燥的,需要坚持!坚持!坚持!当然也需要技巧,第一遍学习的时候,不用关注全部细节,不重要的 ...
- H5bulider中的微信支付配置注意事项
一.云打包安卓自定义证书的生成: 签名算法名称: SHA1withRSA主体公共密钥算法:1024 位 RSA 密钥密钥库类型:JKS 1.下载JDK1.6安装,切换到bin目录,打开命令行: 2.生 ...
- Python基础学习总结__Day4
一.装饰器 前戏: 1.函数即变量 (1)函数名为‘门牌号’即内存地址,加括号开始调用 (2)没有变量调用将清空内存 (3)匿名函数(lambda函数):没有‘门牌号’,需要赋值给变量,否则将会被清空 ...
- python-闭包函数和装饰器
目录 闭包函数 什么是闭包? 两种为函数传参的方式 使用参数的形式 包给函数 闭包函数的应用 闭包的意义: 装饰器 无参装饰器 什么是装饰器 为什么要用装饰器 怎么用装饰器 完善装饰器 闭包函数 什么 ...
- 如何编写自己的C语言头文件
一些初学C语言的人,不知道头文件(*.h文件)原来还可以自己写的.只知道调用系统库函数时,要使用#include语句将某些头文件包含进去.其实,头文件跟.C文件一样,是可以自己写的.头文件是一种文本文 ...
- hdu-2553 N皇后问题(搜索题)
在N*N的方格棋盘放置了N个皇后,使得它们不相互攻击(即任意2个皇后不允许处在同一排,同一列,也不允许处在与棋盘边框成45角的斜线上. 你的任务是,对于给定的N,求出有多少种合法的放置方法. Inpu ...
- poj-3278 catch that cow(搜索题)
题目描述: Farmer John has been informed of the location of a fugitive cow and wants to catch her immedia ...
- 二叉排序树:HDU3791-二叉搜索树(用指针建立二叉排序树)
二叉搜索树 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Problem Descr ...
- Python动态属性和特性(一)
在Python中,数据的属性和处理数据的方法统称为属性.其实,方式只是可调用的属性.除了这二者之外,我们还可以创建特性(property),在不改变类接口的前提下,使用存取方法(即读取值和设置值方法) ...