正文开始 注:JDK版本为1.8

HashMap1.8和1.8之前的源码差别很大


  • 目录

    • 简介

      • 数据结构
    • 类结构
    • 属性
    • 构造方法
    • 增加
    • 删除
    • 修改
    • 总结

1.HashMap简介

HashMap基于哈希表的Map接口实现,是以key-value存储形式存在。(除了不同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。)

HashMap 的实现不是同步的,这意味着它不是线程安全的。它的key、value都可以为null。此外,HashMap中的映射不是有序的。在 JDK1.8 中,HashMap 是由 数组+链表+红黑树构成,新增了红黑树作为底层数据结构,结构变得复杂了,但是效率也变的更高效。

1.2 HashMap数据结构

在 JDK1.8 中,HashMap 是由 数组+链表+红黑树构成,新增了红黑树作为底层数据结构,结构变得复杂了,但是效率也变的更高效。当一个值中要存储到Map的时候会根据Key的值来计算出他的

hash,通过哈希来确认到数组的位置,如果发生哈希碰撞就以链表的形式存储在Object源码分析中解释过,但是这样如果链表过长来的话,HashMap会把这个链表转换成红黑树来存储。

来看依一下HashMap的存储结构

但是这样的话问题来了,HashMap为什么要使用红黑树呢,这样结构的话不是更麻烦了吗??

这个问题我也没有想过,其实很多在看的时候只会在乎红黑树的实现而忽略到了为什么要使用的这个问题,我也是在写本文的时候突发疑惑。参考了网上的例子,同时也解释了为什么阀值为8:

因为Map中桶的元素初始化是链表保存的,其查找性能是O(n),而树结构能将查找性能提升到O(log(n))。当链表长度很小的时候,即使遍历,速度也非常快,但是当链表长度不断变长,肯定会对查询性能有一定的影响,所以才需要转成树。至于为什么阈值是8,我想,去源码中找寻答案应该是最可靠的途径。

参考地址:https://dwz.cn/nPFXmXwJ

2.类结构

我们来看一下类结构

在阅读源码的时候一直有个问题很困惑就是HashMap已经继承了AbstractMap而AbstractMap类实现了Map接口,那为什么HashMap还要在实现Map接口呢?同样在ArrayList中LinkedList中都是这种结构。

据 java 集合框架的创始人Josh Bloch描述,这样的写法是一个失误。在java集合框架中,类似这样的写法很多,最开始写java集合框架的时候,他认为这样写,在某些地方可能是有价值的,直到他意识到错了。显然的,JDK的维护者,后来不认为这个小小的失误值得去修改,所以就这样存在下来了。

  • Cloneable 空接口,表示可以克隆
  • Serializable 序列化
  • AbstractMap 提供Map实现接口

3.属性

初始化容量(必须是二的n次幂)

集合最大容量(必须是二的幂)

负载因子,默认的0.75

当链表的值超过8则会转红黑树(1.8新增)

当链表的值小于6则会从红黑树转回链表

当Map里面的数量超过这个值时,表中的桶才能进行树形化 ,否则桶内元素太多时会扩容,而不是树形化 为了避免进行扩容、树形化选择的冲突,这个值不能小于 4 * TREEIFY_THRESHOLD

table用来初始化(必须是二的n次幂)

用来存放缓存

HashMap中存储的数量

用来记录HashMap的修改次数

用来调整大小下一个容量的值计算方式为(容量*负载因子)

哈希表的加载因子

重点属性

  • table 在JDK1.8中我们了解到HashMap是由数组加链表加红黑树来组成的结构其中table就是HashMap中的数组
  • Size 为HashMap中K-V的实时数量
  • loadFactor 加载因子,是用来衡量 HashMap 满的程度,计算HashMap的实时加载因子的方法为:size/capacity,而不是占用桶的数量去除以capacity。capacity 是桶的数量,也就是 table 的长度length。
  • threshold 计算公式:capacity * loadFactor。这个值是当前已占用数组长度的最大值。过这个数目就重新resize(扩容),扩容后的 HashMap 容量是之前容量的两倍

4.构造方法

开始看构造方法。

4.1 HashMap()

构造一个空的 HashMap ,默认初始容量(16)和默认负载因子(0.75)。

4.2 HashMap(int initialCapacity)

构造一个空的 HashMap具有指定的初始容量和默认负载因子(0.75)。

4.3 HashMap(int initialCapacity, float loadFactor)

构造一个空的 HashMap具有指定的初始容量和负载因子。我们来分析一下。

最后调用了tableSizeFor,来看一下方法实现:

5.增加

现在我们开始分析put()方法

我们可以看到put调用的是putVal来进行数据插入,但是要注意到key在这里执行了一下hash()方法,来看一下Hash方法是如何实现的。

从上面可以得知HashMap是支持Key为空的,而HashTable是直接用过Key来获取HashCode所以key为空会抛异常其实上面就已经解释了为什么HashMap的长度为什么要是2的幂因为HashMap 使用的方法很巧妙,它通过 hash & (table.length -1)来得到该对象的保存位,前面说过 HashMap 底层数组的长度总是2的n次方,这是HashMap在速度上的优化。当 length 总是2的n次方时,hash & (length-1)运算等价于对 length 取模,也就是 hash%length,但是&比%具有更高的效率。比如 n % 32 = n & (32 -1)。

现在看putVal()方法,看看它到底做了什么。

主要参数:

  • hash key的hash值
  • key 原始Key
  • value 要存放的值
  • onlyIfAbsent 如果true代表不更改现有的值
  • evict 如果为false表示table为创建状态

完整源码分析,放图片的话会太长了,所以就截取了一下分为两部。

暂时分析到添加 ,首发乱敲代码公众号

HashMap源码分析(一):JDK源码分析系列的更多相关文章

  1. LinkedList源码分析:JDK源码分析系列

    如果本文中有不正确的地方请指出由于没有留言可以在公众号添加我的好友共同讨论. 1.介绍 LinkedList 是线程不安全的,允许元素为null的双向链表. 2.继承结构 我们来看一下LinkedLi ...

  2. 【JDK源码】将JDK源码导入IDEA中

    新建工程 在IDEA中新建普通JAVA工程,步骤如下: 导入源码 首先可以通过如下方法找到工程目录. 在JDK安装目录下找到源码包src.zip,如下图 将src.zip包解压,并将src目录下的内容 ...

  3. 【JDK】JDK源码分析-Semaphore

    概述 Semaphore 是并发包中的一个工具类,可理解为信号量.通常可以作为限流器使用,即限制访问某个资源的线程个数,比如用于限制连接池的连接数. 打个通俗的比方,可以把 Semaphore 理解为 ...

  4. 【并发编程】【JDK源码】JDK的(J.U.C)java.util.concurrent包结构

    本文从JDK源码包中截取出concurrent包的所有类,对该包整体结构进行一个概述. 在JDK1.5之前,Java中要进行并发编程时,通常需要由程序员独立完成代码实现.当然也有一些开源的框架提供了这 ...

  5. 跟踪调试JDK源码时遇到的问题及解决方法

    目录 问题描述 解决思路 在IntelliJ IDEA中调试JDK源码 在eclipse中调试JDK源码 总结 问题描述 最近在研究MyBatis的缓存机制,需要回顾一下HashMap的实现原理.于是 ...

  6. 关于JDK源码:我想聊聊如何更高效地阅读

    简介 大家好,我是彤哥,今天我想和大家再聊聊JDK源码的几个问题: 为什么要看JDK源码 JDK源码的阅读顺序 JDK源码的阅读方法 为什么要看JDK源码 一,JDK源码是其它所有源码的基础,看懂了J ...

  7. 一言不合就开始搞JDK源码

    ​Java是一门面向对象的编程语言,那什么是面向对象呢,下面将是历史上最通俗易懂的解释了,请看下图: 哈哈,解释的够清楚的了吧.闪. 从源码学编程的好处 学Java编程时,最好同时看一些Java的源码 ...

  8. JDK 源码分析(4)—— HashMap/LinkedHashMap/Hashtable

    JDK 源码分析(4)-- HashMap/LinkedHashMap/Hashtable HashMap HashMap采用的是哈希算法+链表冲突解决,table的大小永远为2次幂,因为在初始化的时 ...

  9. 【JDK】JDK源码分析-HashMap(1)

    概述 HashMap 是 Java 开发中最常用的容器类之一,也是面试的常客.它其实就是前文「数据结构与算法笔记(二)」中「散列表」的实现,处理散列冲突用的是“链表法”,并且在 JDK 1.8 做了优 ...

随机推荐

  1. QWidget继承自QPaintDevice,这样就可以直接把QWidget传入QPainter的构造函数,比如QPainter(mylabel),然后设置QWidget的长宽后直接进行作画了

    比如用QLabel在主界面上画两条虚线: bool ContentWidget::eventFilter(QObject *obj, QEvent *event) { if(obj == line_l ...

  2. jQuery省市联动

    <!DOCTYPE html><html lang="en" xmlns="http://www.w3.org/1999/xhtml"> ...

  3. WPF后台生成datatemplate(TreeViewItem例子)

    public void loadCheckListDataTemplate(TreeViewItem tvi) { DataTemplate cdt = new DataTemplate(); Fra ...

  4. 在WPF中实现图片一边下载一边显示

    原文 在WPF中实现图片一边下载一边显示 当我们上网查看一个较大的图片时,浏览器能一边下载一边显示,这样用户体验是比较好的,但在WPF程序中,当我们通过如下方式显示一幅图片时: img.Source ...

  5. [转]深入Android内存泄露

    深入内存泄露 Android应用的内存泄露,其实就是java虚拟机的堆内存泄漏. 当然,当应用有ndk,jni时,没有及时free,本地堆也会出现内存泄漏. 本文只是针对JVM内存泄漏应用,进行阐述分 ...

  6. Arch Linux 是个 针对 i686 优化的 Linux 发行版(通过可以轻松使用的二进制包系统 - pacman)

    Arch Linux 是个 针对 i686 优化的 Linux 发行版(通过可以轻松使用的二进制包系统 - pacman)Arch 同时也拥有一个类似 ports 的包构建系统(Arch Build ...

  7. c# 叫号小程序

    写个叫号的小demo 长相如下 代码如下 using System; using System.Collections.Generic; using System.ComponentModel; us ...

  8. windows Service 之调试过程(附加到进程里调试,而且启动时间不能超过30秒)

    最近第一次用C#写了一个windows service ,其实实现的内容比较简单.就是启动remoting 连接,但是调试相对初次写windws service 的我来说,比较烦.没有经验,而且没办法 ...

  9. 国人Web前端开发必备干货,一个完美支持IE6在内所有浏览器的CSS框架

    摘要: 企户动CSS框架是一个能够完美支持IE6~7在内的所有浏览器的 HTML&CSS 前端框架!给Web开发提供了自适应宽度的百分比多列网格,以及已语义化和结构化的标题.段落.列表.表格. ...

  10. oracle11g安装时出现程序未找到文件解决办法

    在安装的最后可能会出现如下问题 解决办法如下 将win64_11gR2_database_2of2中的\win64_11gR2_database_2of2\database\stage\Compone ...