Java 中HashMap详解(含HashTable, ConcurrentHashMap)

本篇重点:

1.HashMap的存储结构

2.HashMap的put和get操作过程

3.HashMap的扩容

4.关于transient关键字

5.HashMap, HashTable, ConcurrentHashMap 对照

6.关于volatile关键字

Java 中HashMap详解(含HashTable, ConcurrentHashMap)

HashMap的存储结构

  1. HashMap 总体是数组+链表的存储结构, 从JDK1.8开始,当数组的长度大于64,且链表的长度大于8的时候,会把链表转为红黑树。

  2. 数组的默认长度是16。数组中的每一个元素为一个node,也就是链表的一个节点,node的数据包含: key的hashcode, key, value,指向下一个node节点的指针。

部分源码如下:

static class Node implements Map.Entry {
        final int hash;
        final K key;
        V value;
        Node next;

        Node(int hash, K key, V value, Node next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }
...

}
  1. 随着put操作的进行,如果数组的长度超过64,且链表的长度大于8的时候, 则将链表转为红黑树,红黑树节点的结构如下,TreeNode继承的LinkedHashMap.Entry是继承HashMap.Node的,所以TreeNode是上面Node的子类。
static final class TreeNode extends LinkedHashMap.Entry {
        TreeNode parent;  // red-black tree links
        TreeNode left;
        TreeNode right;
        TreeNode prev;    // needed to unlink next upon deletion
        boolean red;
        TreeNode(int hash, K key, V val, Node next) {
            super(hash, key, val, next);
        }
//...

}
  1. HashMap类的主要成员变量:
    Java 中HashMap详解(含HashTable, ConcurrentHashMap)
/* ---------------- Fields -------------- */

    /**
     * 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[] table;

    /**
     * Holds cached entrySet(). Note that AbstractMap fields are used
     * for keySet() and values().

     */
    transient Set> entrySet;

    /**
     * The number of key-value mappings contained in this map.

     */
    transient int size;

    /**
     * The number of times this HashMap has been structurally modified
     * Structural modifications are those that change the number of mappings in
     * the HashMap or otherwise modify its internal structure (e.g.,
     * rehash).  This field is used to make iterators on Collection-views of
     * the HashMap fail-fast.  (See ConcurrentModificationException).

     */
    transient int modCount;

    /**
     * The next size value at which to resize (capacity * load factor).

     *
     * @serial
     */
    // (The javadoc description is true upon serialization.

    // Additionally, if the table array has not been allocated, this
    // field holds the initial array capacity, or zero signifying
    // DEFAULT_INITIAL_CAPACITY.)
    int threshold;

    /**
     * The load factor for the hash table.

     *
     * @serial
     */
    final float loadFactor;

View Code

HashMap的put操作过程

本小节讲述put操作中的主要步骤,细小环节会忽略。

  1. map.put(key, value),首先计算key的hash,得到一个int值。

2.如果Node数组为空则初始化Node数组。这里注意,Node数组的长度length始终应该是2的n次方,比如默认的16, 还有32,64等

3.用 hash&(length-1) 运算得到数组下标,这里要提一句,其实正常我们最容易想到的,而且也是我之前很长一段时间以为的,这一步应该进行的是求模运算:hash % length,这样得到的正好是0~length-1之间的值,可以作为数组的下标, 那么为何此处是位与运算呢?

先说结论:上面提到数组的长度length始终是2^n,在这个前提下,hash & (length-1) 与hash % length是等价的。 而位与运算更快。这里另开一遍进行详解:HashMap的哈希函数为何用(n – 1) & hash

  1. 如果Node[hash&(length-1)]处为空,用传入的的key, value创建Node对象,直接放入该下标;如果该下标处不为空,且对象为TreeNode类型,证明此下标处的元素们是按照红黑树的结构存储的,将传入的key,value作为新的红黑树的节点插入到红黑树;否则,此处为链表,用next找到链表的末尾,将新的元素插入。如果在遍历链表的过程中发现链表的长度超过了8,此时如果数组长度

Original: https://www.cnblogs.com/adeline-tech/p/16666235.html
Author: adeline.pan
Title: Java 中HashMap详解(含HashTable, ConcurrentHashMap)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/620342/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • xxl-job踩坑记录——执行器,执行10分钟自动失败

    问题描述 上一篇Docker 部署xxl-job 报错:xxl-rpc remoting error(connect timed out), for url : xxxxxx &#…

    Java 2023年6月5日
    094
  • 【Java全栈进阶】-多态

    多态 多态的概述 多态是继封装、继承之后,面向对象的第三大特性。 现实事物经常会体现出多种形态,如学生,学生是人的一种,则一个具体的同学 张三既是学生也是人,即出现两种形态。 Ja…

    Java 2023年6月7日
    051
  • ysoserial URLDNS利用链分析

    对这个测试类Urldns做序列化后,反序列化的时候执行了重写的readobject方法。 所以只要对readobject方法做重写就可以实现在反序列化该类的时候得到执行。 利用链的…

    Java 2023年6月5日
    069
  • 手撕快速排序(含图解和两种实现代码含改进)

    摘要 快速排序其实也是分而治之的思想 快速排序是递归的 首先找一个基准点,把比基准点小的数字都放到它的左边,比它大的数字都放在它的右边,一趟下来基准点的位置找到了,且它左边的数字小…

    Java 2023年6月16日
    062
  • FISCO搭建说明(Ubuntu 20.04)

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    Java 2023年6月8日
    075
  • MySQL学习-eclipse导入jar包

    导包先有包 !!!一定要下载和自己MySQL版本一样的jar包!!! !!!一定要下载和自己MySQL版本一样的jar包!!! !!!一定要下载和自己MySQL版本一样的jar包!…

    Java 2023年6月9日
    063
  • 正则 捕获组之反向引用

    之前写正则的时候,经常用到 (.*?) 之类的用法.一般在替换的时候会用 $1 来引用括号里面匹配到的内容比如, 1.1.1.1 aaaa 2.2.2.2 bbbb 3.3.2.3…

    Java 2023年6月16日
    098
  • Spring 中经典的 9 种设计模式,打死也要记住啊!

    来源:blog.csdn.net/caoxiaohong1005 转载: https://mp.weixin.qq.com/s/HdOKIp_rFgX-h65M0pRK9Q 1.简…

    Java 2023年5月30日
    069
  • spring 使用异步任务

    1.说明 在springboot 中使用 @Async 实现异步任务处理,下面介绍一下如何实现这个。 2.实现代码 2.1 增加@EnableAsync @EnableAsync …

    Java 2023年5月30日
    075
  • Java源码赏析(五)再识 String 类

    在 Java源码赏析(三)初识 String 类 中,我们已经大概理解了String的接口,接下来我们描述一下String的常用工具方法。 /** * 为&#x4E8…

    Java 2023年6月8日
    059
  • 狂神说Spring Boot

    1、Spring Boot 简介 简化Spring应用开发的一个框架;整个Spring技术栈的一个大整合;J2EE开发的一站式解决方案; 2、微服务 2014,martin fow…

    Java 2023年6月7日
    060
  • 面试官:你说说一条查询SQL的执行过程

    为了理解这个问题,先从Mysql的架构说起,对于Mysql来说,大致可以分为3层架构。 第一层作为客户端和服务端的连接,连接器负责处理和客户端的连接,还有一些权限认证之类。比如客户…

    Java 2023年6月13日
    071
  • spring bean依赖注入

    实例化bean对象之后,即在applyMergedBeanDefinitionPostProcessors方法中调用MergedBeanDefinitionPostProcesso…

    Java 2023年6月9日
    073
  • 1、编译系统

    1 编译系统 1.1 引入编译系统 1.2 编译系统的组成 1.2.1 预处理器 1.2.2 编译器 1.2.3 汇编器 1.2.4 链接器 2 GCC 3 编译过程演示 3.1 …

    Java 2023年6月7日
    072
  • JVM内存分配和垃圾回收

    一、JVM堆分代 1、JVM堆被分为了 年轻代和 老年代。年轻代的GC过程称为Yong GC,速度快较频繁。老年代的GC过程称为Full GC,速度较慢应该尽量避免。 2、对象被创…

    Java 2023年6月8日
    083
  • -2020年度钻石C++C(2)《博学谷》

    -2020年度钻石C++C(2)《博学谷》 第一类:数据类型关键字 void 声明函数无返回值或无参数,声明无类型指针,显式丢弃运算结果。 char 字符型类型数据,属于整型数据的…

    Java 2023年6月7日
    0103
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球