JDK1.8_HashMap源码__构造函数
HashMap的底层实现是一个Node类型的数组,也就是说使用put(key, value)方法的时候就把key和value根据hashcode值存在table数组相应的下标中,源码如下:
/**
* The table, initialized on first use, and resized as
* necessary. When allocated, length is always a power of two.
* (We also tolerate length zero in some operations to allow
* bootstrapping mechanics that are currently not needed.)
*/
transient Node<K,V>[] table;
Node是一个实现了Map.Entry的泛型类,源码如下:
/**
* Basic hash bin node, used for most entries. (See below for
* TreeNode subclass, and in LinkedHashMap for its Entry subclass.)
*/
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next; Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
} public final K getKey() { return key; }
public final V getValue() { return value; }
public final String toString() { return key + "=" + value; } public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
} public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
} public final boolean equals(Object o) {
if (o == this)
return true;
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
if (Objects.equals(key, e.getKey()) &&
Objects.equals(value, e.getValue()))
return true;
}
return false;
}
}
2 HashMap的构造函数
HashMap共有四个构造函数:
public HashMap()
public HashMap(int initialCapacity)
public HashMap(int initialCapacity, float loadFactor)
public HashMap(Map<? extends K, ? extends V> m)
2.1 无参构造函数:public HashMap()
/**
* Constructs an empty <tt>HashMap</tt> with the default initial capacity
* (16) and the default load factor (0.75).
*/
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
官方在HashMap()构造方法的注释里说到:“使用默认初始化容量(16)和默认加载因子(0.75)来构造一个空的HashMap”,但是源码中只初始化了loadFactor而没有初始化默认容量,那么容量的初始化在哪里呢?
既然容量在构造函数里没有进行初始化,那么在使用put方法往hashMap中添加元素会发生怎么情况呢?
下面就让我们来put方法中一探究竟吧!
public V put(K key, V value) {
//这里调用了下面的putVal方法
return putVal(hash(key), key, value, false, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K, V>[] tab;
Node<K, V> p;
int n, i;
//如果table为空或者长度为0,先进行扩容
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//省略剩余代码。。。。。。
}
为了不干扰这里先把暂时用不到的代码忽略掉,从源码中的黄色高亮代码中我们可以看到,在put的时候当table为null或者长度为0时将会调用它resize方法进行一次扩容,resize方法中发生了什么呢?
final Node<K, V>[] resize() {
//1. 这里的table由于没有初始化所以依然为null
Node<K, V>[] oldTab = table;
//2. 此时oldCap的值为0
int oldCap = (oldTab == null) ? 0 : oldTab.length;
//3. 获取旧的扩容阈值,此时threshold还没有赋值,因此它的值为默认值0
int oldThr = threshold;
int newCap, newThr = 0;
//如果旧的table中有元素,显然这里不符合条件
if (oldCap > 0) {
//忽略无关代码
} else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int) (DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
if (newThr == 0) {
//5. threshold变成了table数组长度与加载因子的积
float ft = (float) newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float) MAXIMUM_CAPACITY ?
(int) ft : Integer.MAX_VALUE);
}
threshold = newThr;
@SuppressWarnings({"rawtypes", "unchecked"})
Node<K, V>[] newTab = (Node<K, V>[]) new Node[newCap];
table = newTab;
//忽略无关代码
return newTab;
}
注意:这里出现了一个变量threshold,threshold是用来判断table是否需要扩容的阈值,当table中的元素数量大于threshold时table就会进行扩容。
从黄色高亮的代码中可以清楚的看到,resize方法以DEFAULT_INITIAL_CAPACITY为数组大小创建了一个新的数组替换旧的数组。
并将扩容阈值设置为DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY 也就是16*0.75 = 12
2.2 指定初始容量的构造函数:public HashMap(int initialCapacity)
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
源码中可以看到该构造函数调用了指定初始容量和加载因子的构造函数并将加载因子指定为默认值(0.75)。
2.3 指定初始容量和加载因子的构造函数:public HashMap(int initialCapacity, float loadFactor)
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
}
源码中首先对initialCapacity和loadFactor进行了验证,之后设置了扩容阈值threshold的值,(请记住这个变量,待会初始化table数组时还会用到它)
tableSizeFor方法是如何设置threshold的值的呢?这个方法的详细解释在另一篇随笔里:HashMap源码__tableSizeFor方法解析
现在我们只需要知道这个方法会返回一个不小于参数cap的最小的2的整数次幂的数,比如cap为10,就返回16、cap为17,就返回32。
table数组的初始化
通过对以上构造函数源码的解析发现,用于储存元素的table数组都没有在构造函数里初始化,那么table数组什么时候进行初始化呢? 答案就时调用put方法的时候!前面结束无参构造方法时提到过,put方法调用了putVal方法,而putVal方法对table数组进行了扩容,因此下面直接来看resize方法:
final Node<K, V>[] resize() {
//1. 这里的table由于没有初始化所以依然为null
Node<K, V>[] oldTab = table;
//2. 此时oldCap的值为0
int oldCap = (oldTab == null) ? 0 : oldTab.length;
//3. 获取旧的扩容阈值,由于在构造函数里设置过threshol的值,所以这里threshol的值为不小指定容量的最小2的整数次幂
int oldThr = threshold;
int newCap, newThr = 0;
//如果旧的table中有元素,显然这里不符合条件
if (oldCap > 0) {
//忽略无关代码
//4. 重点来了, 由于oldThr已经赋值了,所以会执行方法体中的newCap=oldThr,这样一来table数组的容量就变成了前面提到的threshol的值
} else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int) (DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
if (newThr == 0) {
//5. threshold变成了table数组长度与加载因子的积
float ft = (float) newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float) MAXIMUM_CAPACITY ?
(int) ft : Integer.MAX_VALUE);
}
threshold = newThr;
@SuppressWarnings({"rawtypes", "unchecked"})
Node<K, V>[] newTab = (Node<K, V>[]) new Node[newCap];
table = newTab;
//忽略无关代码
return newTab;
}
JDK1.8_HashMap源码__构造函数的更多相关文章
- 【集合框架】JDK1.8源码分析HashSet && LinkedHashSet(八)
一.前言 分析完了List的两个主要类之后,我们来分析Set接口下的类,HashSet和LinkedHashSet,其实,在分析完HashMap与LinkedHashMap之后,再来分析HashSet ...
- 【JUC】JDK1.8源码分析之ArrayBlockingQueue(三)
一.前言 在完成Map下的并发集合后,现在来分析ArrayBlockingQueue,ArrayBlockingQueue可以用作一个阻塞型队列,支持多任务并发操作,有了之前看源码的积累,再看Arra ...
- 【集合框架】JDK1.8源码分析之HashMap(一) 转载
[集合框架]JDK1.8源码分析之HashMap(一) 一.前言 在分析jdk1.8后的HashMap源码时,发现网上好多分析都是基于之前的jdk,而Java8的HashMap对之前做了较大的优化 ...
- 【集合框架】JDK1.8源码分析之ArrayList详解(一)
[集合框架]JDK1.8源码分析之ArrayList详解(一) 一. 从ArrayList字表面推测 ArrayList类的命名是由Array和List单词组合而成,Array的中文意思是数组,Lis ...
- 集合之TreeSet(含JDK1.8源码分析)
一.前言 前面分析了Set接口下的hashSet和linkedHashSet,下面接着来看treeSet,treeSet的底层实现是基于treeMap的. 四个关注点在treeSet上的答案 二.tr ...
- 集合之LinkedHashSet(含JDK1.8源码分析)
一.前言 上篇已经分析了Set接口下HashSet,我们发现其操作都是基于hashMap的,接下来看LinkedHashSet,其底层实现都是基于linkedHashMap的. 二.linkedHas ...
- 集合之HashSet(含JDK1.8源码分析)
一.前言 我们已经分析了List接口下的ArrayList和LinkedList,以及Map接口下的HashMap.LinkedHashMap.TreeMap,接下来看的是Set接口下HashSet和 ...
- 【1】【JUC】JDK1.8源码分析之ArrayBlockingQueue,LinkedBlockingQueue
概要: ArrayBlockingQueue的内部是通过一个可重入锁ReentrantLock和两个Condition条件对象来实现阻塞 注意这两个Condition即ReentrantLock的Co ...
- 【1】【JUC】JDK1.8源码分析之ReentrantLock
概要: ReentrantLock类内部总共存在Sync.NonfairSync.FairSync三个类,NonfairSync与FairSync类继承自Sync类,Sync类继承自AbstractQ ...
随机推荐
- Nginx的踩坑实录
1.昨天在为一个新项目配置地址转发,搞了很久都没生效,日志也没有问题,但就是没到转发的目标机器上. nginx.conf 配置如下: location /prism{ proxy_pass http: ...
- 用postman导出excel文件
原文地址:https://jingyan.baidu.com/article/915fc414559b4351394b2084.html 现在的web和移动开发,常常会调用服务器提供restful接口 ...
- Xen+OpenQRM快速部署
一.选择系统平台 a) Ubuntu-11.10-server-amd64 二.Xen安装 a) Xen安装 # apt-get -y install xen-hypervisor-4.1-a ...
- C#服务端的GET、POST请求
一.HttpClient方式,程序集 System.Net.Http.dll GET: HttpClient httpClient = new HttpClient(); string result ...
- Python3实现发送邮件和发送短信验证码
Python3实现发送邮件和发送短信验证码 Python3实现发送邮件: import smtplib from email.mime.text import MIMEText from email. ...
- PKU-2723 Get Luffy Out(2-SAT+二分)
Get Luffy Out 题目链接 Ratish is a young man who always dreams of being a hero. One day his friend Luffy ...
- ElasticSearch入门篇Ⅰ --- ES核心知识概括
C01.什么是Elasticsearch 1.什么是搜索 垂直搜索(站内搜索) 互联网的搜索:电商网站,招聘网站,各种app IT系统的搜索:OA软件,办公自动化软件,会议管理,员工管理,后台管理系 ...
- Java之路——初识Eclipse
零.大纲 一.前言 二.获取Eclipse 三.运行Eclipse 四.创建及运行第一个Java Project 五.界面介绍 六.如何调试 七.获取插件 八.Eclipse 快捷键 九.总结 一.前 ...
- HBase学习总结
一.HBase介绍 1.基本概念 HBase是一种Hadoop数据库,经常被描述为一种稀疏的,分布式的,持久化的,多维有序映射,它基于行键.列键和时间戳建立索引,是一个可以随机访问的存储和检索数据的平 ...
- 读《Clean Code 代码整洁之道》之感悟
盲目自信,自认为已经敲了几年代码,还看什么整洁之道啊.我那可爱的书架读懂了我的心思,很明事理的保护起来这本小可爱,未曾让它与我牵手 最近项目中的 bug 有点多,改动代码十分吃力,每看一行代码都带一句 ...