JavaSE_坚持读源码_HashMap对象_put_Java1.7
当你往HashMap里面put时,你其实在干什么?
/**
* Associates the specified value with the specified key in this map.
* If the map previously contained a mapping for the key, the old
* value is replaced.
*
* @param key key with which the specified value is to be associated
* @param value value to be associated with the specified key
* @return the previous value associated with <tt>key</tt>, or
* <tt>null</tt> if there was no mapping for <tt>key</tt>.
* (A <tt>null</tt> return can also indicate that the map
* previously associated <tt>null</tt> with <tt>key</tt>.)
*/
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
// 如果key为空,调用putForNullKey进行处理
if (key == null)
return putForNullKey(value);
//根据key来获得对应的hash值
int hash = hash(key);
// 搜索指定的hash值在对应的表中的索引
int i = indexFor(hash, table.length);
// 如果 i 索引处的 Entry 不为 null,通过循环不断遍历 e 元素的下一个元素
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
// 当entry的hash与上面获得的key的hash相等,并且entry的key与上面的key相等(==或者equals),把value赋值给oldValue
//这里有个问题:当 Hash 冲突严重时,在桶上形成的链表会变的越来越长,这样在查询时的效率就会越来越低;时间复杂度为O(N)
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
} modCount++;
// 如果 i 索引处的 Entry 为 null,表明此处还没有 Entry
// 将 key、value 添加到 i 索引处,并去创建一个entry
addEntry(hash, key, value, i);
return null;
}
根据上面 put 方法的源代码可以看出,当程序试图将一个 key-value 对放入 HashMap 中时,程序首先根据该 key 的 hash() 返回值决定该 Entry 的存储位置:如果两个 Entry 的 key 的 hash() 返回值相同,那它们的存储位置相同。如果这两个 Entry 的 key 通过 equals 比较返回 true,新添加 Entry 的 value 将覆盖集合中原有 Entry 的 value,但 key 不会覆盖。如果这两个 Entry 的 key 通过 equals 比较返回 false,新添加的 Entry 将与集合中原有 Entry 形成 Entry 链,而且新添加的 Entry 位于 Entry 链的头部——具体说明继续看 addEntry() 方法的说明。
当向 HashMap 中添加 key-value 对,由其 key 的 hashCode() 返回值决定该 key-value 对(就是 Entry 对象)的存储位置。当两个 Entry 对象的 key 的 hashCode() 返回值相同时,将由 key 通过 eqauls() 比较值决定是采用覆盖行为(返回 true),还是产生 Entry 链(返回 false)。
上面程序中使用的 table 其实就是一个普通数组,每个数组都有一个固定的长度,这个数组的长度就是 HashMap 的容量。HashMap 包含如下几个构造器:
- HashMap():构建一个初始容量为 16,负载因子为 0.75 的 HashMap。
- HashMap(int initialCapacity):构建一个初始容量为 initialCapacity,负载因子为 0.75 的 HashMap。
- HashMap(int initialCapacity, float loadFactor):以指定初始容量、指定的负载因子创建一个 HashMap。
/**
* Adds a new entry with the specified key, value and hash code to
* the specified bucket. It is the responsibility of this
* method to resize the table if appropriate.
*
* Subclass overrides this to alter the behavior of put method.
*/
void addEntry(int hash, K key, V value, int bucketIndex) {
//如果key-value组成的entry数量超过极限,并且table的bucketIndex索引处不为空
if ((size >= threshold) && (null != table[bucketIndex])) {
//把table的长度扩大到原来的两倍
resize(2 * table.length);
//key == null的hash永远是0,如果key不为null,则会重新hash,并重新定位Entry在table中的位置
hash = (null != key) ? hash(key) : 0;
bucketIndex = indexFor(hash, table.length);
} // 获取指定 bucketIndex 索引处的 Entry
// 将新创建的 Entry 放入 bucketIndex 索引处,并让新的 Entry 指向原来的 Entry
createEntry(hash, key, value, bucketIndex);
}
/**
* Like addEntry except that this version is used when creating entries
* as part of Map construction or "pseudo-construction" (cloning,
* deserialization). This version needn't worry about resizing the table.
*
* Subclass overrides this to alter the behavior of HashMap(Map),
* clone, and readObject.
*/
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++;
}
JavaSE_坚持读源码_HashMap对象_put_Java1.7的更多相关文章
- JavaSE_坚持读源码_HashMap对象_get_Java1.7
当你从HashMap里面get时,你其实在干什么? /** * Returns the value to which the specified key is mapped, * or {@code ...
- JavaSE_坚持读源码_ClassLoader对象_Java1.7
ClassLoader java.lang public abstract class ClassLoader extends Object //类加载器的责任就是加载类,说了跟没说一样 A clas ...
- JavaSE_坚持读源码_Class对象_Java1.7
Java程序在运行时,Java运行时系统一直对所有的对象进行所谓的运行时类型标识.这项信息纪录了每个对象所属的类.虚拟机通常使用运行时类型信息选准正确方法去执行,用来保存这些类型信息的类是Class类 ...
- JavaSE_坚持读源码_ArrayList对象_Java1.7
底层的数组对象 /** * The array buffer into which the elements of the ArrayList are stored. * The capacity o ...
- JavaSE_坚持读源码_HashSet对象_Java1.7
对于 HashSet 而言,它是基于 HashMap 实现的,HashSet 底层采用 HashMap 来保存所有元素,因此 HashSet 的实现比较简单,查看 HashSet 的源代码,可以看到如 ...
- JavaSE_坚持读源码_String对象_Java1.7
/** * Compares this string to the specified object. The result is {@code * true} if and only if the ...
- JavaSE_坚持读源码_Object对象_Java1.7
/** * Returns a hash code value for the object. This method is * supported for the benefit of hash t ...
- [一起读源码]走进C#并发队列ConcurrentQueue的内部世界
决定从这篇文章开始,开一个读源码系列,不限制平台语言或工具,任何自己感兴趣的都会写.前几天碰到一个小问题又读了一遍ConcurrentQueue的源码,那就拿C#中比较常用的并发队列Concurren ...
- Java读源码之ReentrantLock(2)
前言 本文是 ReentrantLock 源码的第二篇,第一篇主要介绍了公平锁非公平锁正常的加锁解锁流程,虽然表达能力有限不知道有没有讲清楚,本着不太监的原则,本文填补下第一篇中挖的坑. Java读源 ...
随机推荐
- python之旅七【第七篇】面向对象之类成员
面向对象的类成员 相关知识点 一 字段 字段包括:普通字段和静态字段,他们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同, 普通字段属于对象 静态字段属于类 class Provinc ...
- centos部署nextcloud
简介 Nextcloud是一套用于创建和使用文件托管服务的客户端-服务器软件.它在功能上类似于Dropbox,虽然Nextcloud是免费的和开源的,允许任何人在私人服务器上安装和操作它.与Dropb ...
- Django+Xadmin打造在线教育系统(五)
课程相关功能实现 课程列表 创建课程相关的urls.py path("course/", include('course.urls', namespace="course ...
- ubuntu 16.04 主题美化及终端美化
如果你使用的是图形界面,你会发现ubuntu默认的界面真是丑的一批,所以简单美化一下: 1.安装unity-tweak-tool: sudo apt-get install unity-tweak-t ...
- P2521 [HAOI2011]防线修建
题目链接:P2521 [HAOI2011]防线修建 题意:给定点集 每次有两种操作: 1. 删除一个点 (除开(0, 0), (n, 0), 与指定首都(x, y)) 2. 询问上凸包长度 至于为什么 ...
- SpringBoot 从application.yml中通过@Value读取不到属性值
package cn.exrick.xboot.mqtt; import org.eclipse.paho.client.mqttv3.*;import org.eclipse.paho.client ...
- 自学Python4.2-装饰器
自学Python之路-Python基础+模块+面向对象自学Python之路-Python网络编程自学Python之路-Python并发编程+数据库+前端自学Python之路-django 自学Pyth ...
- PHP-FPM安装报错解决
PHP源码安装 setenforce 0--------------------------------------------------------------------安装php时的报错che ...
- [BJWC2018]Border 的四种求法(后缀自动机+链分治+线段树合并)
题目描述 给一个小写字母字符串 S ,q 次询问每次给出 l,r ,求 s[l..r] 的 Border . Border: 对于给定的串 s ,最大的 i 使得 s[1..i] = s[|s|-i+ ...
- PWM实现ADC和DAC
一.PWM实现AD 利用普通单片机的2个IO及一个运算放大器即可实现AD转换电路,而且很容易扩展成多通道.其占用资源少,成本低,AD 转换精度可以达到8位甚至更高,因此具有一定的实用价值. 1.1 硬 ...