深入理解HashMap底层实现原理
一、HashMap (JDK8)put(K key, V value)底层实现
1. 首先判断这个hashmap是否为空,为空就初始化一个hashmap
2. 根据key 计算hashcode()值,和数组长度-1 进行&(等价于求余), 得到bucket的位置
3. 得到位置后 判断该处的值是否为空 如果为空 直接插入
4. 如果不为空,判断数组后面链接的是否是一棵树,是树就将其put到这颗树中。
5. 不是树,先挂在链表后面,然后判断链表大小是否大于8,大于8就要把链表转换成树
源码:
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
/**
* Implements Map.put and related methods
*
* @param hash hash for key
* @param key the key
* @param value the value to put
* @param onlyIfAbsent if true, don't change existing value
* @param evict if false, the table is in creation mode.
* @return previous value, or null if none
*/
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;
/*如果table的在(n-1)&hash的值是空,就新建一个节点插入在该位置*/
if ((p = tab[i = (n - 1) & hash]) == null)//&相当于取余
tab[i] = newNode(hash, key, value, null);
/*表示有冲突,开始处理冲突*/
else {
Node<K,V> e;
K k;
/*检查第一个Node,p是不是要找的值*/
if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)//数组后面是否为树,如果是树则将其put到树中
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);
//如果冲突的节点数已经达到8个,看是否需要改变冲突节点的存储结构,
//treeifyBin首先判断当前hashMap的长度,如果不足64,只进行
//resize,扩容table,如果达到64,那么将冲突的存储结构为红黑树
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
/*如果有相同的key值就结束遍历*/
if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
/*就是链表上有相同的key值*/
if (e != null) { // existing mapping for key,就是key的Value存在
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;//返回存在的Value值
}
}
++modCount;
/*如果当前大小大于门限,门限原本是初始容量*0.75*/
if (++size > threshold)
resize();//扩容两倍
afterNodeInsertion(evict);
return null;
}
二、扩容
hashmap 默认大小16,每次扩大两倍(增加一倍),加载因子默认是0.75,当 hashmap容量大于 数组长度 * 加载因子的时候就会扩容,也就是初始达到12 的时候就会扩容
扩容时先重建一个2倍长度的数组,然后重新 hash
加载因子表示数组填充的程度,加载因子越大填充的越满,空间利用率越高,但冲突越大,加载因子越小,空间利用率越低,但冲突小,一般平衡在0.75
为什么每次数组的大小都是2的次幂?
1.在计算哈希桶下标的时候hash值和 数组长度减一按位与,数组长度是2的倍数减一二进制表示都是1,速度快;
2. hash & (array.length-1)相当于对array.length-1 取模运算,也就是 % 运算,&效率比%高;
3.数组长度是2的次幂,不同key求的hash值相同几率很小,冲突小,查询快
三、HashMap和HashTable的区别
1. hashmap 的 key 可以为 null,hashtable不行,为空时,hashcode默认为0。
2. hashtable是线程安全的,方法前面都有 synchronized 关键字。
3. hashtable继承Dictionary 类,hashmap继承 AbstractMap类,但都实现了 Map、Cloneable、Serializable接口
四、如何保证HashMap线程安全
1. 使用ConcurrentHashMap
2. Collections.synchronizedMap(new HashMap<String, Integer>());
深入理解HashMap底层实现原理的更多相关文章
- HashMap底层实现原理/HashMap与HashTable区别/HashMap与HashSet区别(转)
HashMap底层实现原理/HashMap与HashTable区别/HashMap与HashSet区别 文章来源:http://www.cnblogs.com/beatIteWeNerverGiveU ...
- Java面试必问之Hashmap底层实现原理(JDK1.7)
1. 前言 Hashmap可以说是Java面试必问的,一般的面试题会问: Hashmap有哪些特性? Hashmap底层实现原理(get\put\resize) Hashmap怎么解决hash冲突? ...
- HashMap底层实现原理/HashMap与HashTable区别/HashMap与HashSet区别
①HashMap的工作原理 HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象.当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算h ...
- Java中HashMap底层实现原理(JDK1.8)源码分析
这几天学习了HashMap的底层实现,但是发现好几个版本的,代码不一,而且看了Android包的HashMap和JDK中的HashMap的也不是一样,原来他们没有指定JDK版本,很多文章都是旧版本JD ...
- HashMap底层实现原理
HashMap底层实现 HashMap底层数据结构如下图,HashMap由“hash函数+数组+单链表”3个要素构成 通过写一个迷你版的HashMap来深刻理解 MyMap接口,定义一个接口,对外暴露 ...
- (转)HashMap底层实现原理/HashMap与HashTable区别/HashMap与HashSet区别
①HashMap的工作原理 HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象.当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算h ...
- HashMap底层实现原理(JDK1.8)源码分析
ref:https://blog.csdn.net/tuke_tuke/article/details/51588156 http://www.cnblogs.com/xiaolovewei/p/79 ...
- HashMap底层实现原理以及HashMap与HashTable区别以及HashMap与HashSet区别
①HashMap的工作原理 HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象.当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算h ...
- 【转载】HashMap底层实现原理及面试问题
①HashMap的工作原理 HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象.当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算h ...
随机推荐
- Android官方架构组件介绍之ViewModel(三)
ViewModel 像Activity,Fragment这类应用组件都有自己的生命周期并且是被Android的Framework所管理的.Framework可能会根据用户的一些操作和设备的状态对Act ...
- iframe高度自适应,自适应子页面高度
html <iframe id="mainFrame" name="mainFrame" scrolling="no" src=&qu ...
- input类型为file改变默认按钮样式
改变 input file 样式(input 文件域)是很多前端朋友经常遇到的头疼问题,今天推荐两种改变 input file 样式的两种常用方法: 方法一: <input type=&quo ...
- mysql主从数据库错误处理
方法一:忽略错误后,继续同步 该方法适用于主从库数据相差不大,或者要求数据可以不完全统一的情况,数据要求不严格的情况 解决: stop slave; #表示跳过一步错误,后面的数字可变set glob ...
- php高级教程
PHP - 多维数组 多维数组指的是包含一个或多个数组的数组. 注释:数组的维度指示您需要选择元素的索引数. 对于二维数组,您需要两个索引来选取元素 对于三维数组,您需要三个索引来选取元素 PHP - ...
- Entity Framework Many to Many Relation Mapping(Entity Framework多对多关系映射)
通常我们在做数据库设计时都会有两张表是多对多关系的时候,在数据库做多对多关系时候我们通常通过中间关联表来处理,那我们现在在EF中是如何处理的呢? 假设我们有如下关系,用户(User)包含多个角色(Ro ...
- Win2D 官方文章系列翻译 - 与 Direct2D 互操作
本文为个人博客备份文章,原文地址: http://validvoid.net/win2d-interop-with-direct2d/ Win2D 作为 Direct2D 的上层实现,支持与其进行双向 ...
- jQuery的定时执行和延迟执行
jQuery的定时执行和延迟执行 //延迟执行 setTimeout(function(){ console.log("实战授课,100%就业"); },600); //定时执行 ...
- pymongo模块
import pymongo # 创建与MongoDB服务器的连接 mongoclient = pymongo.MongoClient(host='127.0.0.1', port=27017) # ...
- 转:ACCESS数据库转ORACLE数据库分享
来源: 作者:zz 网上有很多文章介绍access转oracle数据库的方法,本人都尝试了,不是很成功,列举一下,后来人不必盲目试了,基本不成功: 1.Access-->excel-->P ...