HashMap源码之构造函数--JDK1.8
构造函数
变量解释
- capacity,表示的是hashmap中桶的数量,初始化容量initCapacity为16,第一次扩容会扩到64,之后每次扩容都是之前容量的2倍,所以容量每次都是2的次幂
- loadFactor,负载因子,衡量hashmap一个满的程度,初始默认为0.75
- threshold,hashmap扩容的一个标准,每当size大于这个标准时就会进行扩容操作,threeshold等于capacity*loadfacfactor
HashMap(int initialCapacity, float loadFactor)
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegalinitial 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
//最后一行调用的tableSizeFor(initialCapacity);如下
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
tableSizeFor(...)方法的作用在于找到大于等于initialCapacity的最小的2的幂。n右移一位再同自身进行或操作使得n的最高位和最高位的下一位都为1.
n |= n >>> 1;
比如n=9,则n=1001(2),n-1=100
1001
0100
-----
1100
这是因为或操作的要求为相同位上只要存在1则结果为1,全为0结果才为0.一个数最高位为1,右移一位后的数最高位也为1,不论相对应的位上另一个数是多少结果依旧为1.
而对于n |= n >>> 2
n的最高位及其下一位都为1,右移两位后再进行或操作将使得得到的n的前4位都为1,类似的n |= n >>> 4
使得前8位为1,n |= n >>> 8
使得前16位为1。当然并不是说该方法调用下来就会使得返回的值是一个32位都为1的数字,这是因为当右移的位数操作实际的有效位数后是不会对数字产生任何变化的。
以n=9为例,在进过两次右移后n=1111(2)
00000000000000000000000000001111 n (int 32位)
00000000000000000000000000000000 n>>>4
--------------------------------
00000000000000000000000000001111
当n右移4位后全部变成了0,使得结果不会发生任何变化,后面的n |= n >>> 8等等也一样
在最后返回的时候做了一个判断(n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1
,正常来说返回的是一个n+1,在前面的操作中已经将前几位有效的数字变成了1(n=1111),这里加1使得n=10000(2),即变为大于等于initialCapacity的最小的2的幂,到这里貌似就差不多了,但是在tableSizeFor(...)
方法的第一句有一个减法操作int n = cap - 1;
这是为什么呢?这里减1实际上是为处理传入的数本身就是2的幂的情况。比如我传入的是n=8=1000(2),如果不进行减1操作,得到的则是n=10000(2)=16,实际上我需要的就是8.
HashMap(Map<? extends K, ? extends V> m)
public HashMap(Map<? extends K, ? extends V> m) {
this.loadFactor = DEFAULT_LOAD_FACTOR;
putMapEntries(m, false);
}
这个构造函数主要是用一个已有的map来构造一个新的HashMap,这里主要在于putMapEntries(m, false);
中。
final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
int s = m.size();
if (s > 0) {
if (table == null) { // pre-size
float ft = ((float)s / loadFactor) + 1.0F;
int t = ((ft < (float)MAXIMUM_CAPACITY) ?
(int)ft : MAXIMUM_CAPACITY);
if (t > threshold)
threshold = tableSizeFor(t);
}
else if (s > threshold)
resize();
for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
K key = e.getKey();
V value = e.getValue();
putVal(hash(key), key, value, false, evict);
}
}
}
前面几行是做容错判断,当table没有初始化或者传入的map容量大于当前map的阈值(threshold),都需要重新进行设置。从for循环开始则是将传入的map的值插入到当前map中。
HashMap源码之构造函数--JDK1.8的更多相关文章
- HashMap 源码分析 基于jdk1.8分析
HashMap 源码分析 基于jdk1.8分析 1:数据结构: transient Node<K,V>[] table; //这里维护了一个 Node的数组结构: 下面看看Node的数 ...
- HashMap 源码详细分析(JDK1.8)
一.概述 本篇文章我们来聊聊大家日常开发中常用的一个集合类 - HashMap.HashMap 最早出现在 JDK 1.2中,底层基于散列算法实现.HashMap 允许 null 键和 null 值, ...
- hashmap源码解析,JDK1.8和1.7的区别
背景:hashmap面试基础必考内容,需要深入了解,并学习其中的相关原理.此处还要明白1.7和1.8不通版本的优化点. Java 8系列之重新认识HashMap Java 8系列之重新认识HashMa ...
- HashMap源码分析-基于JDK1.8
hashMap数据结构 类注释 HashMap的几个重要的字段 hash和tableSizeFor方法 HashMap的数据结构 由上图可知,HashMap的基本数据结构是数组和单向链表或红黑树. 以 ...
- HashMap源码解读(jdk1.8)
1.相关常量 默认初始化容量(大小) static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; 最大容量 static final int M ...
- HashMap源码解析(JDK1.8)
package java.util; import sun.misc.SharedSecrets; import java.io.IOException; import java.io.Invalid ...
- HashMap源码之常用方法--JDK1.8
常用方法 hash(key) static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCo ...
- JDK1.8 HashMap源码
序言 触摸本质才能永垂不朽 HashMap底层是基于散列算法实现,散列算法分为散列再探测和拉链式.HashMap 则使用了拉链式的散列算法,并在JDK 1.8中引入了红黑树优化过长的链表.数据结构示意 ...
- JDK1.8 HashMap源码分析
一.HashMap概述 在JDK1.8之前,HashMap采用数组+链表实现,即使用链表处理冲突,同一hash值的节点都存储在一个链表里.但是当位于一个桶中的元素较多,即hash值相等的元素较多时 ...
随机推荐
- Springboot & Mybatis 构建restful 服务五
Springboot & Mybatis 构建restful 服务五 1 前置条件 成功执行完Springboot & Mybatis 构建restful 服务四 2 restful ...
- docker服务各个模块
docker容器官网:https://hub.docker.com/ 一.centos7.4中指定安装docker版本 1)默认yum源安装的docker版本为docker1.3.性能偏低,不支持k8 ...
- cerr与cout区别
语言:C++ 一.简介 平常常会用的主要有三个:cout.cerr.clog,首先简单介绍下三者. 这三者在C++中都是标准IO库中提供的输出工具(至于有关的重载问题在此不讨论): cout:写到标准 ...
- 01 C语言程序设计--01 C语言基础--第3章 基本数据类型01
01.1.3.1序言 00:02:17 01.1.3.2 C语言中的基本元素和常量的概念 00:08:54 01.1.3.3示例--常量 00:12:08 01.1.3.4变量的概念和命名规则 00: ...
- MyBatis generator配置 overwrite 文件覆盖失效
工具:IDEA.jdk1.8.mysql 底部有解决方法! pom.xml配置 <plugins> <!--Mybatis自动代码插入--> <plugin> &l ...
- php数组排序sort
php的数组分为数字索引型的数组,和关键字索引的数组.如果是数字索引的,可以这样使用:$names = ['Tom', 'Rocco','amiona'];sort($names);sort()函数只 ...
- css样式表的知识点总结
css总的来说有三种css样式可供选择: 1,行内样式表 行内样式表,直接写在了html文件的元素中,例如: <div style="color:red;"></ ...
- C语言的转义字符
原文地址:http://blog.163.com/sunshine_linting/blog/static/44893323201181325818165/ 在字符集中,有一类字符具有这样的特性:当从 ...
- 【慕课网实战】九、以慕课网日志分析为例 进入大数据 Spark SQL 的世界
即席查询普通查询 Load Data1) RDD DataFrame/Dataset2) Local Cloud(HDFS/S3) 将数据加载成RDDval masterLog = sc.textFi ...
- BZOJ4720-换教室
题目很长,是一道概率dp题,一般需要逆推,但这题结局不确定所以要顺推. 用f[i][j][k],i表示第i段时间,j表示用了j次申请,k就表示这轮是否用申请. 那么要求min(f[n][0~m][0] ...