HashMap put、get方法源码分析
HashMap.java的实现是面试必问的问题。
JDK版本
java version "1.8.0_91"
Java(TM) SE Runtime Environment (build 1.8.0_91-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.91-b15, mixed mode)
1. HashMap节点的封装
Node<K, V>
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
//.....
}
hash值算法
public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}
put null的key,计算hash值
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
ok,重点分析一下扩容
/**
* Initializes or doubles table size. If null, allocates in
* accord with initial capacity target held in field threshold.
* Otherwise, because we are using power-of-two expansion, the
* elements from each bin must either stay at same index, or move
* with a power of two offset in the new table.
*
* @return the table
*/
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
if (oldCap > 0) {
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
}
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
if (oldTab != null) {
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
if ((e = oldTab[j]) != null) {
oldTab[j] = null;
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e;
else if (e instanceof TreeNode)
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // preserve order
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
next = e.next;
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}
首先说几点
1. 找tab位置,使用的key.hash() & (capacity - 1)
注意这里用的是位运算与操作,这里与的是capacity - 1,这样结果就不可能超过capactity - 1。不仅保证了结果正确,性能也提供了很多
2. 扩容大小为原来的大小左移一位,即扩大为原来的两倍
扩容主要的移动原来的元素,到新的数组里面
这里源码使用了比较好的方法,用key的hash值与capacity进行与,注意不是capacity - 1。如果与的结果是0,说明在新数组中的位置为原来的位置,不用变。如果结果不为0,最高为1,即capacty,说明在新数组中的位置为原来位置 + 原来的capacity
以前面试也被问道过,当时自信满满的胡说。自认为了解的很清楚,这里纠正一下
1. 封装的节点为Node,不是Entity
2. 扩容为大小为原来的两倍
3. 计算在数组位置,不是取余,是采用位操作。hashcode & capactiy - 1,注意这里的 - 1操作
4. put null的key,这里是可以的,null的hash值为0
5. key的计算为object.hashcode() ^ (object.hashcode >>> 16)
先到这
p.s.https://tech.meituan.com/java-hashmap.html这个写的不错
HashMap put、get方法源码分析的更多相关文章
- hashmap的put方法源码分析
put主源码如下: public V put(K key, V value) { if (key == null) return putForNullKey(value); int hash = ha ...
- 【转】HashMap实现原理及源码分析
哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景极其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,而HashMap的实现原理也常常出 ...
- HashMap实现原理及源码分析之JDK8
继续上回HashMap的学习 HashMap实现原理及源码分析之JDK7 转载 Java8源码-HashMap 基于JDK8的HashMap源码解析 [jdk1.8]HashMap源码分析 一.H ...
- java-通过 HashMap、HashSet 的源码分析其 Hash 存储机制
通过 HashMap.HashSet 的源码分析其 Hash 存储机制 集合和引用 就像引用类型的数组一样,当我们把 Java 对象放入数组之时,并非真正的把 Java 对象放入数组中.仅仅是把对象的 ...
- 每天学会一点点(HashMap实现原理及源码分析)
HashMap实现原理及源码分析 哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希 ...
- Java split方法源码分析
Java split方法源码分析 public String[] split(CharSequence input [, int limit]) { int index = 0; // 指针 bool ...
- invalidate和requestLayout方法源码分析
invalidate方法源码分析 在之前分析View的绘制流程中,最后都有调用一个叫invalidate的方法,这个方法是啥玩意?我们来看一下View类中invalidate系列方法的源码(ViewG ...
- Linq分组操作之GroupBy,GroupJoin扩展方法源码分析
Linq分组操作之GroupBy,GroupJoin扩展方法源码分析 一. GroupBy 解释: 根据指定的键选择器函数对序列中的元素进行分组,并且从每个组及其键中创建结果值. 查询表达式: var ...
- HashMap实现原理及源码分析
哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,而HashMap的实现原理也常常出 ...
- HashMap实现原理及源码分析(JDK1.7)
转载:https://www.cnblogs.com/chengxiao/p/6059914.html 哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技 ...
随机推荐
- 洛谷P2762 太空飞行计划问题(最小割)
传送门 我们可以把实验放在左边,仪器放在右边,点有点权,然后连对应的有向边,就是求一个最大权闭合图,可以转化为最小割来做(关于这具体是个啥……可以百度胡伯涛<最小割模型在信息学竞赛中的应用> ...
- CAP理论中的P到底是个什么意思
在CAP理论中,C代表一致性,A代表可用性(在一定时间内,用户的请求都会得到应答),P代表分区容错.这里分区容错到底是指数据上的多个备份还是说其它的 ? 我感觉分布式系统中,CAP理论应该是C和A存在 ...
- 【FAQ】服务下线
原因:磁盘已满
- 6、C++共用体
6.共用体 共用体(union)是一种数据格式,他能够存储不同的数据类型,但只能同时存储其中的一种类型.也就是说,结构可以同时存储int.long和double,共用体只能存储ing.long.dou ...
- C# 获取类中属性注释值
转 http://bbs.csdn.net/topics/350019800 PropertyInfo[] peroperties = typeof(TEST).GetProperties(Bindi ...
- 22.Container With Most Water(能装最多水的容器)
Level: Medium 题目描述: Given n non-negative integers a1, a2, ..., an , where each represents a point ...
- CentOS 中安装nginx
Centos6.8 yum 安装 nginx 1:使用yum安装nginx,安装nginx库 [root@hadoop110 //]# rpm -Uvh http://nginx.org/pack ...
- 网络控制芯片AX88796B系列使用简介
目录 1. 特性 2. 结构框图 3. 接收 3.1 缓存空间 3.2 Receiver Buffer Ring 3.3 接收机制 4. 发送 5. 编程过程简要说明 5.1 初始化配置 5.2 接收 ...
- Navicat 连接Sqlite数据库的方法和步骤
1.打开Navicat,进行如下操作. 2.在弹出的新建连接对话框中输入正确的信息,点击“确定”按钮. 3.可见如下
- Win10 修改 开始 菜单样式..
因为不是平板,所以改成了这个样子 下面说步骤... 打开 菜单栏位置... 将快捷方式 拷贝到 里面 来... 快捷方式 以 #开头.是为了 让其排列在最前面.... 快捷方式有个技巧...快捷方式 ...