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读源 ...
随机推荐
- Nginx 网络事件
L27-29 应用层(如浏览器等一系列组成的发送get请求) 传输层 系统内核打开一个端口将客户端IP及端口和服务端IP及端口记录下来一并传输到网络层 网络层 打包后到链路层 再到客户端路由器至广域网
- Nginx upstream变量
log_format varups '$upstream_addr $upstream_connect_time $upstream_header_time $upstream_response_ti ...
- Django-urls路由系统
Django的路由系统 Django 1.11版本 URLConf官方文档 URL配置(URLconf)就像Django 所支撑网站的目录.它的本质是URL与要为该URL调用的视图函数之间的映射表. ...
- Linux下的好用的编辑软件Remarkable
Linux下的好用的编辑软件Remarkable最近着手开始学习Linux,就想着找一款好用的编辑器作笔记,在网上爬了些贴选择了Remarkable.官网崩了,有没有梯子,废了好大力气才装好.于是把资 ...
- Volatile的应用
.java 的执行过程 Java代码在编译后会变成Java字节码 字节码被类加载器加载到JVM里 JVM执行字节码,转化为汇编指令在CPU上执行 Java中所使用的并发机制依赖于JVM的实现和CPU的 ...
- Tomcat启动特慢之SecureRandom问题解决
tomcat启动日志: 08-Jun-2018 09:23:00.445 WARNING [localhost-startStop-1] org.apache.catalina.util.Sessio ...
- MD5进行解密操作
package com.dyy.test; import java.security.MessageDigest; public class TestMD5Util { /*** * MD5加码 生成 ...
- Dividing POJ - 1014 多重背包二进制优化
多重背包模型 写的时候漏了一个等号找了半天 i<<=1 !!!!!! #include<iostream> #include<cstdio> #include&l ...
- yoj维护
维护 启动容器 docker start yoj 暂停容器 docker stop yoj 重启容器 docker restart yoj 进入容器的终端 docker attach yok 保存容器 ...
- 【BZOJ1419】Red is good 期望DP
题目大意 桌面上有\(R\)张红牌和\(B\)张黑牌,随机打乱顺序后放在桌面上,开始一张一张地翻牌,翻到红牌得到\(1\)美元,黑牌则付出\(1\)美元.可以随时停止翻牌,在最优策略下平均能得到多少钱 ...