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)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技 ...
随机推荐
- JavaScript:Browser 对象
ylbtech-JavaScript:Browser 对象 1. Window 对象返回顶部 1. Window 对象 Window 对象 Window 对象表示浏览器中打开的窗口. 如果文档包含框 ...
- [javaEE] Eclipse 默认设置的换行长度
eclipse 默认设置的换行长度, 格式化代码后,经常换行,非常难看. 1.Java代码 打开Eclipse的Window菜单,然后 Preferences->Java->Code St ...
- 框架,表格,表单元素,css基础以及基本标签的结合
<head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8&quo ...
- BZOJ 4032 trie树+各种乱搞
思路 : 先对b 的所有后缀建立trie树 第一问 暴力枚举a串的起点 在trie树上跑 找到最短的 第二问 也是暴力枚举a串的起点 a和b顺着暴力匹配就好 第三问 求出来a在第i个位置 加一个字母j ...
- JavaScript全局函数
JavaScript 中包含以下 7 个全局函数,用于完成一些常用的功能(以后的章节中可能会用到):escape( ).eval_r( ).isFinite( ).isNaN( ).parseFloa ...
- CSS3伪元素、伪类选择器
伪元素选择器: ::first-letter:为某个元素中的文字的首字母或第一个字使用样式. ::first-line:为某个元素的第一行文字使用样式. ::before:在某个元素之前插入一些内容. ...
- ADODB.RecordSet常用方法查询
rs = Server.CreateObject("ADODB.RecordSet") rs.Open(sqlStr,conn,1,A) 注:A=1表示读取数据:A=3表示新增.修 ...
- Python 之 基础知识(五)
一.变量 1.引用 id() 函数传参 与 返回值 都是传递保存的数据的引用 2.可变和不可变类型(变量的引用地址只在赋值语句后变化) 不可变类型 内存中的数据不允许被修改 数字类型 int,bool ...
- python爬虫:读取PDF
下面的代码可以实现用python读取PDF,包括读取本地和网络上的PDF. pdfminer下载地址:https://pypi.python.org/packages/source/p/pdfmine ...
- Laravel5 开启Debug
Laravel默认没有开启Debug,打开需要两步: .env文件中设置APP_DEBUG=true: 把Laravel服务重启一下(摸索出来的). 这只是开启了基本的调试模式,如果需要看更详细的调试 ...