hashmap的put方法源码分析
put主源码如下:
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
1.1、putForNullKey():当key为null的时候自动转向putForNullKey()方法,用来处理null键,将他们放到table[0]的位置,下面的是putForNullKey()方法,进来遍历entry链表,如果遍历后,发现没有key没为nulll存在,则直接添加一个entry,键为null,值为新的value;而当遍历后,发现key有null的时候,就返回就值,让新值覆盖旧值。
private V putForNullKey(V value) {
for (Entry<K,V> e = table[0]; e != null; e = e.next) {
if (e.key == null) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(0, null, value, 0);
return null;
}
1.2当传入的键不为null的时候,先hash一下key的hashcode值(用来避免重写hashcode方法的不完美,保证hash散布的均匀),然后使用indexFor方法来找到对应在table[]上的位置。
indexFor源码:它没有对hash表的长度取余而使用了位运算来得到索引,这是为什么呢?因为length在hashmap中默认为2的幂次方,所以length-1所得到的二进制都是1构成的,所以hash和这个length-1做与运算其实也是对hash表的长度取余。位运算快于四则运算。(这也是为什么要去size为2的幂次方的原因,因为如果取其他数值,hash碰撞几率增大,可能size的二进制某一位上是0,导致好几个table位置无法存放数据,造成空间浪费)。
static int indexFor(int h, int length) {
return h & (length-1);
}
1.3、找到新元素的table[index]后,就遍历该位置上的entry链;如果仅仅使用equals进行链上比较效率会很低,所以我们先使用hash来比较过滤。遇到相同的值就覆盖,返回旧值。如果没有相同值就直接addentry()进行头插法插入链表。
1.4、增加entry方法addentry():addentry的源码:判断当前table的size大于等于边界值并且index位置上不为空,就2倍扩容table他的大小,然后再根据传入的hash,key,value重新定向这个元素的位置。创建entry并创建。
void addEntry(int hash, K key, V value, int bucketIndex) {
if ((size >= threshold) && (null != table[bucketIndex])) {
resize(2 * table.length);
hash = (null != key) ? hash(key) : 0;
bucketIndex = indexFor(hash, table.length);
}
createEntry(hash, key, value, bucketIndex);
}
1.4扩容resize(扩容条件,当前容量大于边界值,并且添加的index对应的table上不为null)
void resize(int newCapacity) {
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
Entry[] newTable = new Entry[newCapacity];
transfer(newTable, initHashSeedAsNeeded(newCapacity));
table = newTable;
threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
}
1.5、关键是空表扩容后的拷贝到新表的方法transfer方法,下面是源码:将table上的entry进行遍历,传入的rehash用来判断是否用来重新hash,为true的话就判断key是否为null,如果为null就hash值为0,不为null就重hash,然后重定位index,然后头插法放到对应的位置上
void transfer(Entry[] newTable, boolean rehash) {
int newCapacity = newTable.length;
for (Entry<K,V> e : table) {
while(null != e) {
Entry<K,V> next = e.next;
if (rehash) {
e.hash = null == e.key ? 0 : hash(e.key);
}
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
}
}
}
1.6、最后进行createEntry(),头插法插入,最后将e传入新table[]中,表示指针指向旧值。
void createEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<>(hash, key, value, e);
size++;
}
至此全部完成
hashmap的put方法源码分析的更多相关文章
- 【转】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)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技 ...
随机推荐
- Dungeon Master hdoj
Dungeon Master Time Limit : 2000/1000ms (Java/Other) Memory Limit : 131072/65536K (Java/Other) Tot ...
- 表格td内容过多时,td显示省略号,鼠标移入显示全部内容。
转自:https://blog.csdn.net/weixin_42193908/article/details/80405014 两种方式显示: 1.title方式显示: <!DOCTYPE ...
- 快速排序c++实现 快排C++代码实现
快速排序c++实现 快排C++ 第一.算法描述 快速排序由C. A. R. Hoare在1962年提出,该算法是目前实践中使用最频繁,实用高效的最好排序算法, 快速排序算法是采用分治思想的算法,算法分 ...
- AtCoder Beginner Contest 054
1. A - One Card Poker 水题,直接输出大小. 2. B - Template Matching 暴力,每个位置枚举,比较. 3. C - One-stroke Path n的大小只 ...
- logging (日志) 模块
本文源自景女神 函数式简单配置 import logging logging.debug('debug message') logging.info('info message') logging.w ...
- Linux下实现视频读取
V4L(video4linux是一些视频系统,视频软件.音频软件的基础,经常时候在需要采集图像的场合,如视频监控,webcam,可视电话,经常使用在embedded linux中是linux嵌入式开发 ...
- vue中子组件需调用父组件通过异步获取的数据
原因:子组件需要调用父组件传过来的数据,如果这个数据是异步从接口里取的,那这个组件在任何生命周期里都取不到,而应该在接口调取后取到. 需要在msg拿到值后才调用组件,然后你在生命周期created里面 ...
- layui新手使用
1,首先最重要的是引入官方的layui.js layui.css文件 2,在自己的项目中新建一个目录 再在该目录下建一个js文件,js中写入 layui.define(['layer', 'form ...
- List 常用方法解析
1.Count属性 (获得List中元素数目) 2.Add( ) 在List中添加一个对象的公有方法 3.AddRange( ) 公有方法,在List尾部添加实现了ICollection接口的多个元素 ...
- Django学习笔记----settings and database_based App demo
原文参考docs.djangoproject.com, what can manage.py utility do? find here. 1.Database Setup 编辑settings.py ...