构造函数

变量解释

  1. capacity,表示的是hashmap中桶的数量,初始化容量initCapacity为16,第一次扩容会扩到64,之后每次扩容都是之前容量的2倍,所以容量每次都是2的次幂
  2. loadFactor,负载因子,衡量hashmap一个满的程度,初始默认为0.75
  3. 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 >>> 2n的最高位及其下一位都为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的更多相关文章

  1. HashMap 源码分析 基于jdk1.8分析

    HashMap 源码分析  基于jdk1.8分析 1:数据结构: transient Node<K,V>[] table;  //这里维护了一个 Node的数组结构: 下面看看Node的数 ...

  2. HashMap 源码详细分析(JDK1.8)

    一.概述 本篇文章我们来聊聊大家日常开发中常用的一个集合类 - HashMap.HashMap 最早出现在 JDK 1.2中,底层基于散列算法实现.HashMap 允许 null 键和 null 值, ...

  3. hashmap源码解析,JDK1.8和1.7的区别

    背景:hashmap面试基础必考内容,需要深入了解,并学习其中的相关原理.此处还要明白1.7和1.8不通版本的优化点. Java 8系列之重新认识HashMap Java 8系列之重新认识HashMa ...

  4. HashMap源码分析-基于JDK1.8

    hashMap数据结构 类注释 HashMap的几个重要的字段 hash和tableSizeFor方法 HashMap的数据结构 由上图可知,HashMap的基本数据结构是数组和单向链表或红黑树. 以 ...

  5. HashMap源码解读(jdk1.8)

    1.相关常量 默认初始化容量(大小) static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; 最大容量 static final int M ...

  6. HashMap源码解析(JDK1.8)

    package java.util; import sun.misc.SharedSecrets; import java.io.IOException; import java.io.Invalid ...

  7. HashMap源码之常用方法--JDK1.8

    常用方法 hash(key) static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCo ...

  8. JDK1.8 HashMap源码

    序言 触摸本质才能永垂不朽 HashMap底层是基于散列算法实现,散列算法分为散列再探测和拉链式.HashMap 则使用了拉链式的散列算法,并在JDK 1.8中引入了红黑树优化过长的链表.数据结构示意 ...

  9. JDK1.8 HashMap源码分析

      一.HashMap概述 在JDK1.8之前,HashMap采用数组+链表实现,即使用链表处理冲突,同一hash值的节点都存储在一个链表里.但是当位于一个桶中的元素较多,即hash值相等的元素较多时 ...

随机推荐

  1. 初识STM32中的USMART组件

    今天看了usmart那部分的模块,感觉使我们stm32的学习变更加方便,你可以通过串口查看和检验你所注册过的函数. USMART配步骤1.将USMART包添加到工程中,头文件要包括path2.添加所需 ...

  2. Self Attention需要掌握的基本原理

    字面意思理解,self attention就是计算句子中每个单词的重要程度. 1. Structure 通过流程图,我们可以看出,首先要对输入数据做Embedding 1. 在编码层,输入的word- ...

  3. tp5

    tp5.1创建模块 把build.php放在应用目录下面, 然后打开cmd, cd../../ cd phpstudy/www/tp5 php think build tp5.1控制器 return ...

  4. Python开发——13.操作系统、进程和线程

    一.操作系统 1.定义 操作系统是用来协调.管理和控制计算机硬件和软件资源的系统程序,它位于硬件和应用程序之间.操作系统运行在内核态,拥有对所有硬件的完全访问权,可以执行机器能够运行的任何指令.软件的 ...

  5. 使用vmware vconverter从物理机迁移系统到虚拟机P2V(多图)

    zhuan:https://segmentfault.com/a/1190000002697929 本文完整记录了如何从物理服务器,保持所有环境配置信息,纹丝不动的迁移到虚拟机上,俗称 P2V .采用 ...

  6. search

    |—search()—|—添加一个列表变量Expend,存储每个小格扩展时为第几步,可打印出 |                    |—打印运动表 |—A*—|— heuristic() |—Dy ...

  7. Forward团队-爬虫豆瓣top250项目-开发文档

    项目地址:https://github.com/xyhcq/top250 我在本次项目中负责写爬虫中对数据分析的一部分,根据马壮分析过的html,我来进一步写代码获取数据,具体的功能及实现方法我已经写 ...

  8. eclipse设置新建jsp文件默认字符编码为utf-8

    在使用Eclipse开发中,编码默认是ISO-8859-1,不支持中文.这样我们每次新建文件都要手动修改编码,非常麻烦.其实我们可以设置文件默认编码,今后再新建文件时就不用修改编码了. 1.打开Ecl ...

  9. max (Largest elements in array)

    句法: M = max(A) M = max(A,[],dim) [M,I] = max(___) C = max(A,B) ___ = max(___,nanflag)   描述: M=max(A) ...

  10. Shell输入和输出功能-3