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方法源码分析的更多相关文章

  1. 【转】HashMap实现原理及源码分析

    哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景极其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,而HashMap的实现原理也常常出 ...

  2. HashMap实现原理及源码分析之JDK8

    继续上回HashMap的学习 HashMap实现原理及源码分析之JDK7 转载 Java8源码-HashMap  基于JDK8的HashMap源码解析  [jdk1.8]HashMap源码分析 一.H ...

  3. java-通过 HashMap、HashSet 的源码分析其 Hash 存储机制

    通过 HashMap.HashSet 的源码分析其 Hash 存储机制 集合和引用 就像引用类型的数组一样,当我们把 Java 对象放入数组之时,并非真正的把 Java 对象放入数组中.仅仅是把对象的 ...

  4. 每天学会一点点(HashMap实现原理及源码分析)

    HashMap实现原理及源码分析   哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希 ...

  5. Java split方法源码分析

    Java split方法源码分析 public String[] split(CharSequence input [, int limit]) { int index = 0; // 指针 bool ...

  6. invalidate和requestLayout方法源码分析

    invalidate方法源码分析 在之前分析View的绘制流程中,最后都有调用一个叫invalidate的方法,这个方法是啥玩意?我们来看一下View类中invalidate系列方法的源码(ViewG ...

  7. Linq分组操作之GroupBy,GroupJoin扩展方法源码分析

    Linq分组操作之GroupBy,GroupJoin扩展方法源码分析 一. GroupBy 解释: 根据指定的键选择器函数对序列中的元素进行分组,并且从每个组及其键中创建结果值. 查询表达式: var ...

  8. HashMap实现原理及源码分析

    哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,而HashMap的实现原理也常常出 ...

  9. HashMap实现原理及源码分析(JDK1.7)

    转载:https://www.cnblogs.com/chengxiao/p/6059914.html 哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技 ...

随机推荐

  1. Dungeon Master hdoj

    Dungeon Master Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 131072/65536K (Java/Other) Tot ...

  2. 表格td内容过多时,td显示省略号,鼠标移入显示全部内容。

    转自:https://blog.csdn.net/weixin_42193908/article/details/80405014 两种方式显示: 1.title方式显示: <!DOCTYPE ...

  3. 快速排序c++实现 快排C++代码实现

    快速排序c++实现 快排C++ 第一.算法描述 快速排序由C. A. R. Hoare在1962年提出,该算法是目前实践中使用最频繁,实用高效的最好排序算法, 快速排序算法是采用分治思想的算法,算法分 ...

  4. AtCoder Beginner Contest 054

    1. A - One Card Poker 水题,直接输出大小. 2. B - Template Matching 暴力,每个位置枚举,比较. 3. C - One-stroke Path n的大小只 ...

  5. logging (日志) 模块

    本文源自景女神 函数式简单配置 import logging logging.debug('debug message') logging.info('info message') logging.w ...

  6. Linux下实现视频读取

    V4L(video4linux是一些视频系统,视频软件.音频软件的基础,经常时候在需要采集图像的场合,如视频监控,webcam,可视电话,经常使用在embedded linux中是linux嵌入式开发 ...

  7. vue中子组件需调用父组件通过异步获取的数据

    原因:子组件需要调用父组件传过来的数据,如果这个数据是异步从接口里取的,那这个组件在任何生命周期里都取不到,而应该在接口调取后取到. 需要在msg拿到值后才调用组件,然后你在生命周期created里面 ...

  8. layui新手使用

    1,首先最重要的是引入官方的layui.js  layui.css文件 2,在自己的项目中新建一个目录 再在该目录下建一个js文件,js中写入 layui.define(['layer', 'form ...

  9. List 常用方法解析

    1.Count属性 (获得List中元素数目) 2.Add( ) 在List中添加一个对象的公有方法 3.AddRange( ) 公有方法,在List尾部添加实现了ICollection接口的多个元素 ...

  10. Django学习笔记----settings and database_based App demo

    原文参考docs.djangoproject.com, what can manage.py utility do? find here. 1.Database Setup 编辑settings.py ...