关于Java HashMap的那些你不清楚的的事情

哈希流星
• 阅读 2501

大热的《阿里巴巴Java开发规约》中有提到:

【推荐】集合初始化时,指定集合初始值大小。
说明:HashMap使用如下构造方法进行初始化,如果暂时无法确定集合大小,那么指定默认值(16)即可:
public HashMap (int initialCapacity) {
    this(initialCapacity, DEFAULT_LOAD_FACTOR);
}

看到代码规约这一条的时候,我觉得是不是有点太 low 了,身为开发,大家都知道 HashMap 的原理。

什么?这个要通过插件监测?没必要吧,哪个开发不知道默认大小,何时 resize 啊,然后我和孤尽打赌随机咨询几位同学以下几个问题:

HashMap 默认bucket数组多大?
如果new HashMap<>(19),bucket数组多大?
HashMap 什么时候开辟bucket数组占用内存?
HashMap 何时扩容?
抽样调查的结果出乎我的意料:

HashMap 默认bucket数组多大?(答案是16,大概一半的同学答错)
如果new HashMap<>(19),bucket数组多大?(答案是32,大多被咨询同学都不太了解这个点)
HashMap 什么时候开辟bucket数组占用内存?(答案是第一次 put 时,一半同学认为是 new 的时候)
HashMap 何时扩容?(答案是put的元素达到容量乘负载因子的时候,默认16*0.75,有1/4同学中枪)
HashMap 是写代码时最常用的集合类之一,看来大家也不是全都很了解。孤尽乘胜追击又抛出问题:JDK8中 HashMap 和之前 HashMap 有什么不同?

我知道 JDK8 中 HashMap 引入了红黑树来处理哈希碰撞,具体细节和源代码并没有仔细翻过,看来是时候对比翻看下 JDK8 和 JDK7 的 HashMap 源码了。

通过对比翻看源码,先说下结论:

  1. HashMap 在 new 后并不会立即分配bucket数组,而是第一次 put 时初始化,类似 ArrayList 在第一次 add 时分配空间。
  2. HashMap 的 bucket 数组大小一定是2的幂,如果 new 的时候指定了容量且不是2的幂,实际容量会是最接近(大于)指定容量的2的幂,比如 new HashMap<>(19),比19大且最接近的2的幂是32,实际容量就是32。
  3. HashMap 在 put 的元素数量大于 Capacity LoadFactor(默认16 0.75) 之后会进行扩容。
  4. JDK8在哈希碰撞的链表长度达到TREEIFY_THRESHOLD(默认8)后,会把该链表转变成树结构,提高了性能。
  5. JDK8在 resize 的时候,通过巧妙的设计,减少了 rehash 的性能消耗。

存储结构

JDK7 中的 HashMap 还是采用大家所熟悉的数组+链表的结构来存储数据。

JDK8 中的 HashMap 采用了数组+链表或树的结构来存储数据。

重要参数

HashMap中有两个重要的参数,容量(Capacity) 和 负载因子(Load factor)

Initial capacity: The capacity is the number of buckets in the hash table, The initial capacity is simply the capacity at the time the hash table is created.
Load factor: The load factor is a measure of how full the hash table is allowed to get before its capacity is automatically increased.

Initial capacity 决定 bucket 的大小,Load factor 决定 bucket 内数据填充比例,基于这两个参数的乘积,HashMap 内部由 threshold 这个变量来表示 HashMap 能放入的元素个数。

  • Capacity 就是 HashMap 中数组的 length
  • loadFactor 一般都是使用默认的0.75
  • threshold 决定能放入的数据量,一般情况下等于 Capacity * LoadFactor

以上参数在 JDK7 和 JDK8中是一致的,接下来会根据实际代码分析。

JDK8 中的 HashMap 实现

以下内容见原文
https://yq.aliyun.com/article...

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
4年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Wesley13 Wesley13
4年前
java集合系列之HashMap源码
java集合系列之HashMap源码HashMap的源码可真不好消化!!!首先简单介绍一下HashMap集合的特点。HashMap存放键值对,键值对封装在Node(代码如下,比较简单,不再介绍)节点中,Node节点实现了Map.Entry。存放的键值对的键不可重复。jdk1.8后,HashMap底层采用的是数组加链表、红黑树的数据结构,因此实现起
Wesley13 Wesley13
4年前
java8新特性
Stream将List转换为Map,使用Collectors.toMap方法进行转换背景:User类,类中分别有id,name,age三个属性。List集合,userList,存储User对象1、指定keyvalue,value是对象中的某个属性值。 Map<Integer,StringuserMap1userList.str
Wesley13 Wesley13
4年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
4年前
Java 8新特性之Stream 概念
Java8中有两大最为重要的改变。第一个是Lambda表达式;另外一个则是StreamAPI(java.util.stream.\)。Stream是Java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用StreamAPI对集合数据进行操作,就类似于使用SQL执行
Wesley13 Wesley13
4年前
Java 类及类的构造方法
类类是一个模子,确定对象将会拥有的特性(属性)和行为(方法)。类的特点  类时对象的类型  具有相同属性和方法的一组对象的集合构造方法作用就是对类进行初始化。如果你没有定议任何构造方法的形式,Java会为你取一个不带任何参数的构造函数,那么你产生类的对像时只能用不带参数的方法,如:classa{}//没
Stella981 Stella981
4年前
HashMap 的底层实现原理
HashMap是一个用于存储KeyValue键值对的集合,每一个键值对也叫做Entry。这些个Entry分散存储在一个数组当中,这个数组就是HashMap的主干。HashMap数组每一个元素的初始值都是Null。 !(https://oscimg.oschina.net/oscnet/8495d30fe00a2865dd74088d2
Wesley13 Wesley13
4年前
Java 之 HashMap 集合
一、HashMap概述java.util.HashMap<k,v集合implementsMap<k,v接口HashMap集合的特点:1、HashMap集合底层是哈希表:查询速度特别的快JDK1.8之前:数组单向链表JDK1.8之后:数组单向链表|红黑树(