深入理解HashMap 的工作原理及代码实现,什么时候用到红黑树

HashMap 常见问题Java8(扩容、转红黑树、退化链表、Hash优化) HashMap扩容 HashMap HashMap树化 HashMap退化链表 HashMap hash优化 HashMap put过程 HashMap 转红黑树条件 HashMap 循环异常 ConcurrentModificationException 红黑树、链表 阅读详情

GitHub:https://github.com/JDawnF

一.HashMap的内部结构(线程不安全,基于jdk1.7):

  • hashmap是无序的,因为每次根据 key 的 hashcode 映射到 Entry 数组上,所以遍历出来的顺序并不是写入的顺序
  • HashMap 底层是基于数组和链表实现的,如图所示,其中两个重要的参数:容量和负载因子;容量的默认大小是 16,负载因子是 0.75,当 HashMap 的 size > 16*0.75 时就会发生扩容(容量和负载因子都可以自由调整)。 
  • 内部包含了一个 Entry 类型的数组 table。HashMap的主干是一个Entry数组。Entry是HashMap的基本组成单元,每一个Entry包含一个key-value键值对。

    //HashMap的主干数组,可以看到就是一个Entry数组,初始值为空数组{},主干数组的长度一定是2的次幂
     transient Node<K,V>[] table;
    

    static class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;
        Entry<K,V> next;//存储指向下一个Entry的引用,单链表结构
        int hash;//对key的hashcode值进行hash运算后得到的值,存储在Entry,避免重复计算
         
        //创建新的Entry
        Entry(int h, K k, V v, Entry<K,V> n) {
            value = v;
            next = n;
            key = k;
            hash = h;
        }
    
        public final K getKey() {
            return key;
        }
    
        public final V getValue() {
            return value;
        }
    
        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }
    
        public final boolean equals(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry e = (Map.Entry)o;
            Object k1 = getKey();
            Object k2 = e.getKey();
            if (k1 == k2 || (k1 != null && k1.equals(k2))) {
                Object v1 = getValue();
                Object v2 = e.getValue();
                if (v1 == v2 || (v1 != null && v1.equals(v2)))
                    return true;
            }
            return false;
        }
    
        public final int hashCode() {
            return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue());
        }
    
        public final String toString() {
            return getKey() + "=" + getValue();
        }
    }

    Entry 存储着键值对。它包含了四个字段,从 next 字段我们可以看出 Entry 是一个链表。即数组中的每个位置被当成一个桶,一个桶存放一个链表。HashMap 使用拉链法来解决冲突,同一个链表中存放哈希值相同的 Entry。

  • 简单来说,HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的,如果定位到的数组位置不含链表(当前entry的next指向null),那么对于查找,添加等操作很快,仅需一次寻址即可;如果定位到的数组包含链表,对于添加操作,其时间复杂度为O(n),首先遍历链表,存在即覆盖,否则新增;对于查找操作来讲,仍需遍历链表,然后通过key对象的equals方法逐一比对查找。所以,性能考虑,HashMap中的链表出现越少,性能才会越好。

    
    //实际存储的key-value键值对的个数
    transient int size;
    //阈值,当table == {}时,该值为初始容量(初始容量默认为16);当table被填充了,也就是为table分配内存空间后,threshold一般为 capacity*loadFactory。HashMap在进行扩容时需要参考threshold,后面会详细谈到
    int threshold;
    //负载因子,代表了table的填充度有多少,默认是0.75
    final float loadFactor;
    //用于快速失败,由于HashMap非线程安全,在对HashMap进行迭代时,如果期间其他线程的参与导致HashMap的结构发生变化了(比如put,remove等操作),需要抛出异常ConcurrentModificationException
    transient int modCount;

     

二、拉链法的工作原理(解决hash冲突)

在哈希表中进行插入和查找操作同理,先通过哈希函数计算出实际存储地址,然后从数组中对应地址取出即可。所谓哈希冲突是指两个元素通过哈希函数计算出来的值是相同的,即当我们对某个元素进行哈希运算,得到一个存储地址,然后要进行插入的时候,发现已经被其他元素占用了,其实这就是所谓的哈希冲突。所以,好的哈希函数会尽可能地保证 计算简单和散列地址分布均匀。

哈希冲突的解决方案有多种:开放定址法(发生冲突,继续寻找下一块未被占用的存储地址),再散列函数法,链地址法,而HashMap即是采用了链地址法,也就是数组+链表的方式。

  • 新建一个 HashMap,默认大小为 16;

  • 插入 <K1,V1> 键值对,先计算 K1 的 hashCode 为 115,使用除留余数法得到所在的桶下标 115%16=3。
  • 插入 <K2,V2> 键值对,先计算 K2 的 hashCode 为 118,使用除留余数法得到所在的桶下标 118%16=6。
  • 插入 <K3,V3> 键值对,先计算 K3 的 hashCode 为 118,使用除留余数法得到所在的桶下标 118%16=6,插在 <K2,V2> 前面。
  • 应该注意到链表的插入是以头插法方式进行的,例如上面的 <K3,V3> 不是插在 <K2,V2> 后面,而是插入在链表头部。
  • 查找需要分成两步进行:

    • 计算键值对所在的桶;
    • 在链表上顺序查找,时间复杂度显然和链表的长度成正比。
  • HashMap<String, String> map = new HashMap<>();
    map.put("K1", "V1");
    map.put("K2", "V2");
    map.put("K3", "V3");

    下面的桶对应数组的一个元素,即数组中的每个位置被当成一个桶,一个桶放一个链表。

  • put方法:首先会将传入的 Key 做hash运算计算出 hashcode,然后根据数组长度取模计算出在数组中的 index 下标。

    由于在计算中位运算比取模运算效率高的多,所以 HashMap 规定数组的长度为 2^n 。这样用 2^n - 1 做位运算与取模效果一致,并且效率还要高出许多。

    由于数组的长度有限,所以难免会出现不同的 Key 通过运算得到的 index 相同,这种情况可以利用链表来解决,HashMap 会在 table[index]处形成链表,采用头插法将数据插入到链表中。

    public V put(K key, V value) {
    //如果table数组为空数组{},进行数组填充(为table分配实际内存空间),入参为threshold,此时threshold为initialCapacity 默认是1<<4(24=16)
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        //如果key为null,存储位置为table[0]或table[0]的冲突链上
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key);//对key的hashcode进一步计算,确保散列均匀
        // 确定桶下标,返回数组下表
        int i = indexFor(hash, table.length);
        // 先找出是否已经存在键为 key 的键值对,如果存在的话就更新(覆盖)这个键值对的值为 value
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
    
        modCount++;//保证并发访问时,若HashMap内部结构发生变化,快速响应失败
        // 插入新键值对,即增加新的entry
        addEntry(hash, key, value, i);
        return null;
    }

    HashMap 允许插入键为 null 的键值对。但是因为无法调用 null 的 hashCode() 方法,也就无法确定该键值对的桶下标,只能通过强制指定一个桶下标来存放。HashMap 使用第 0 个桶存放键为 null 的键值对。

    private V putForNullKey(V value) {
        for (Entry<K,V> e = table[0]; e != null; e = e.next) {
            if (e.key == null) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        modCount++;
        addEntry(0, null, value, 0);
        return null;
    }

    使用链表的头插法,也就是新的键值对插在链表的头部,而不是链表的尾部。

    void addEntry(int hash, K key, V value, int bucketIndex) {
        if ((size >= threshold) && (null != table[bucketIndex])) {
            resize(2 * table.length);//当size超过临界阈值threshold,并且即将发生哈希冲突时进行扩容
            hash = (null != key) ? hash(key) : 0;
            bucketIndex = indexFor(hash, table.length);
        }
    
        createEntry(hash, key, value, bucketIndex);
    }
    
    void createEntry(int hash, K key, V value, int bucketIndex) {
        Entry<K,V> e = table[bucketIndex];
        // 头插法,链表头部指向新的键值对
        table[bucketIndex] = new Entry<>(hash, key, value, e);
        size++;
    }
    Entry(int h, K k, V v, Entry<K,V> n) {
        value = v;
        next = n;
        key = k;
        hash = h;
    }
    

    resize是扩容,默认扩为原来的2倍大小。通过addEntry的代码能够得知,当发生哈希冲突并且size大于阈值的时候,需要进行数组扩容,扩容时,需要新建一个长度为之前数组2倍的新的数组,然后将当前的Entry数组中的元素全部传输过去,扩容后的新数组长度为之前的2倍,所以扩容相对来说是个耗资源的操作。

三、具体实现:

首先,先看看inflateTable:

private void inflateTable(int toSize) {
        int capacity = roundUpToPowerOf2(toSize);//capacity一定是2的次幂
;//此处为threshold赋值,取capacity*loadFactor和MAXIMUM_CAPACITY+1的最小值,capaticy一定不会超过
        threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1)MAXIMUM_CAPACITY,除非loadFactor大于1
        table = new Entry[capacity];
        initHashSeedAsNeeded(capacity);
    }

inflateTable这个方法用于为主干数组table在内存中分配存储空间,通过roundUpToPowerOf2(toSize)可以确保capacity为大于或等于toSize的最接近toSize的二次幂,比如toSize=13,则capacity=16;to_size=16,capacity=16;to_size=17,capacity=32.

private static int roundUpToPowerOf2(int number) {
        // assert number >= 0 : "number must be non-negative";
        return number >= MAXIMUM_CAPACITY
                ? MAXIMUM_CAPACITY
                : (number > 1) ? Integer.highestOneBit((number - 1) << 1) : 1;
    }

roundUpToPowerOf2中的这段处理使得数组长度一定为2的次幂,Integer.highestOneBit是用来获取最左边的bit(其他bit位为0)所代表的数值.

hash函数:

//这是一个神奇的函数,用了很多的异或,移位等运算,对key的hashcode进一步进行计算以及二进制位的调整等来保证最终获取的存储位置尽量分布均匀
final int hash(Object k) {
        int h = hashSeed;
        if (0 != h && k instanceof String) {
            return sun.misc.Hashing.stringHash32((String) k);
        }

        h ^= k.hashCode();

        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }

以上hash函数计算出的值,通过indexFor进一步处理来获取实际的存储位置

/**
     * 返回数组下标
     */
    static int indexFor(int h, int length) {
        return h & (length-1);
    }

 h&(length-1)保证获取的index一定在数组范围内,举个例子,默认容量16,length-1=15,h=18,转换成二进制计算为

最终计算出的index=2。有些版本的对于此处的计算会使用 取模运算,也能保证index一定在数组范围内,不过位运算对计算机来说,性能更高一些(HashMap中有大量位运算)

 

注意:

  • 在并发环境下使用 HashMap 容易出现死循环。

  • 并发场景发生扩容,调用 resize() 方法里的 rehash() 时,容易出现环形链表。这样当获取一个不存在的 key 时,计算出的 index 正好是环形链表的下标时就会出现死循环。

  • 在 JDK1.8 中对 HashMap 进行了优化: 当 hash 碰撞之后写入链表的长度超过了阈值(默认为8),链表将会转换为红黑树。假设 hash 冲突非常严重,一个数组后面接了很长的链表,此时重新的时间复杂度就是 O(n) 。如果是红黑树,时间复杂度就是 O(logn) 。

此外,JDK1.8以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。

JDK1.8

static final int TREEIFY_THRESHOLD = 8;    // 超过8就转为红黑树
static final int UNTREEIFY_THRESHOLD = 6;    // 少于6红黑树则转为链表

https://zhuanlan.zhihu.com/p/21673805

http://www.cnblogs.com/chengxiao/p/6059914.html

源码分析---HashMap中链表和红黑树的转换阈值 HashMap JDK1.8 以前HashMap的实现是 数组+链表 JDK1.8 开始HashMap的实现是 数组+链表+红黑树 JDK1.8以前,HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。 从上图中可以看出,HashMap 底层就是一个数组结构,数组中的每一项又是一个链表。当新建一个 HashMap 的时候,就会初始化一个数组。 JDK1.8后,HashMa... 阅读详情

相关推荐

深入理解HashMap原理(一)——HashMap源码解析(JDK 1.8)

介绍 HashMap原理是JAVA和Android面试中经常会遇到的问题,这篇文章将通过HashMap在JDK1.7和1.8 中的源码来解析HashMap的原理。 相关源码 数组 采用一段连续的存储单元来存储数据。对于指定下标的查找,时间复杂度为O(1);通过给定值进行查找,需要遍历数组,逐一比对给定关键字和数组元素,时间复杂度为O(n),当然,对于有序数组,则可采用二分查找,插值查找,斐波那契查...

紫雾凌寒 5252

HashMap-红黑树

先简单了解下红黑树 红黑树本质上是一种二叉查找树,但它在二叉查找树的基础上额外添加了一个标记(颜色),同时具有一定的规则。这些规则使红黑树保证了一种平衡,插入、删除、查找的最坏时间复杂度都为 O(logn)。 红黑树的特性: 每个节点要么是红色,要么是黑色; 根节点永远是黑色的; 所有的叶节点都是是黑色的(注意这里说叶子节点其实是上图中的 NIL 节点); 每个红色节点的两个子节点一定都是黑色; 从任一节点到其子树中每个叶子节点的路径都包含相同数量的黑色节点; 左旋右旋: 红黑树的左右旋是比

L.的博客 1587

一文带你速通HashMap底层核心数据结构红黑树

每个节点或者红色,或者是黑色。根节点为黑色。每一个叶子节点都是黑色。如果一个节点是红色,那么他的孩子节点都是黑色。从任意一个节点,经过的黑色节点是一样的。在《算法4》一书中认为红黑树和2-3树是等价的。

禅与计算机程序设计的艺术 2454

HashMap原理浅析(关于红黑树是什么?)

HashMap 1. HashMap的数据结构 数据结构中有数组和链表来实现对数据的存储,但这两者基本上是两个极端。 数组 数组存储区间是连续的,占用内存严重,故空间复杂的很大。但数组的二分查找时间复杂度小,为O(1);数组的特点是:寻址容易,插入和删除困难; 链表 链表存储区间离散,占用内存比较宽松,故空间复杂度很小,但时间复杂度很大,达O(N)。链表的特点是:寻址困难,插入和删除容易。 哈希表 那么我们能不能综合两者的特性,做出一种寻址容易,插入删除也容易的数据结构?答案是肯定的,这就是我们要提起的哈希

weixin_45105261的博客 4902

高速设计注意事项总结——MDI篇

在以太网高速通信系统中,MDI(Medium Dependent Interface,介质相关接口)是连接物理层芯片(PHY)与传输介质(如网线)的关键接口,其设计直接影响信号完整性、传输速率和抗干扰能力。本文围绕 MDI 及 RJ45 接口电路、关键参数、设计与布线要点展开,为高速以太网设计提供参考。本文包含MDI 及 RJ45 接口电路组成、电路设计注意事项、布局布线要求等内容

qq_16237389的博客 1445

低速信号设计之 ESPI 篇

ESPI 是 Intel 推出的增强型串行外设接口,基于 SPI 发展而来,新增 Reset# 和 Alert# 信号,采用点对点主从通信模型,最高速率达 33MHz,支持半双工与 DMA 传输。 其物理接口含 10 条线路,通信协议分物理层、数据链路层等四层。布局布线需关注信号完整性、电源完整性,还得考虑与 SPI 的兼容性。 ESPI 广泛应用于计算机系统(连接 BIOS、电源管理器等)和工业控制领域,具有引脚少、功耗低等优势,但设计复杂且存在兼容性挑战。

qq_16237389的博客 1374

HashMap底层红黑树原理(超详细图解)+手写红黑树代码

在看完了小刘老师和黑马的源码视频之后,我整理了一篇HashMap的底层源码文章,学海无涯,这几天看了对红黑树的讲解,故将其整理出来 HashMap底层源码解析上 HashMap底层源码解析下 视频链接 小刘老师讲源码 传智播客-黑马程序员 文章目录红黑树什么是树 红黑树 什么是树 树(tree)是一种抽象数据类型(ADT),用来模拟具有树状结构性质的数据集合,它是由n(n>0)个有限节点通过连接它们的边组成一个具有层次关系的集合。把它叫做“树”是因为他看起来像一颗倒挂的树,也就是说它是根朝上,而叶向

wenwenaier的博客 2万+

深入浅出DDR系列(二)--DDR工作原理

学习完了DDR的基本组成和硬件结构原理之后,从整体上了解了DDR的特性和静态图,包括原理、管脚图等。那么本章就要从动态的角度来分析时序结构,包括read/write的整个过程到数据返回发生了什么,其中包括以下内容 DDR是如何完成Read、Write 等的操作 DDR的基本命令 DDR的时序参数 DDR的性能分析 1. DRAM基本组成 对于DRAM,其主要由行和列组成,每一个bit中都是由类似右下图的类晶体管的结构组成,对于sdram的数据,可以通过控制column和row就可以访问sd

奇小葩 3万+

HashMap红黑树原理及源码分析---图形、注释一应俱全

一、红黑树定义 红黑树是一种结点带有颜色属性的二叉查找树,除此还有以下5大性质: 节点是红色或黑色。 根是黑色。 所有叶子都是黑色(叶子是NIL节点,这类节点不可以忽视,否则代码会看不懂)。 每个红色节点必须有两个黑色的子节点。(从每个叶子到根的所有路径上不能有两个连续的红色节点。) 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点(黑色平衡)。 NIL节点:为了红黑树平衡而添加的空节点 二、节点新增原理: 如果插入的是第一个节点(根节点),红色变黑色。-----基

持续学习,共同进步 7619

MIT-BEVFusion系列六--bevfusion网络部分流程理解

简述bevfusion网络部分

duanmushuangquan的博客 4890

VMware Converter 6.2.0保姆级教程:从物理机到ESXi主机的完整迁移指南(含常见错误排查)

本文提供VMware Converter 6.2.0从物理机迁移至ESXi主机的完整保姆级教程。详细解析了迁移前的环境规划、核心转换过程步骤、版本差异对比,并重点针对代理安装失败、任务卡顿、虚拟机无法启动等常见错误提供了实战排查与解决方案,确保P2V迁移顺利进行。

sam99的博客 379

深入理解 HashMap

什么是 HashMap? ​ HashMap 是基于哈希表的 Map 接口是实现的。此实现提供所有可选操作,并允许使用 null 做为值(key)和键(value)。HashMap 不保证映射的顺序,特别是它不保证该顺序恒久不变。此实现假定哈希函数将元素适当的分布在各个桶之间,可作为基本操作(get 和 put)提供稳定的性能。在jdk1.7中的HashMap是基于数组+链表实现的,在jdk1....

Woo_home的博客 3万+

2025年7月12日全国青少年信息素养大赛图形化(Scratch)编程小学低年级组复赛真题+答案解析

2025年7月12日全国青少年信息素养大赛图形化(Scratch)编程小学低年级组复赛真题+答案解析

伶俐角少儿编程 709

HashMap底层实现原理概述

1. 前言 在一场面试中最能打动面试官的其实是细节,候选人对细节的了解程度决定了留给面试官的印象到底是“基础扎实”还是“基础薄弱”,如果候选人能够举一反三主动阐述自己对一些技术细节的理解和总结,那无疑是面试过程中的一大亮点。HashMap是一个看着简单,但其实里面有很多技术细节的数据结构,在一场高端的面试中即使不问任何红黑树(Java 8中HashMap引入了红黑树来处理极端情况下的哈希碰撞)相关的问题,也会有很多的技术细节值得挖掘。 2. 把书读薄 在Java 7中HashMap实现有1000多行,

傅红雪的专栏 3万+

人工智能智能体研究综述:从理论架构到前沿应用

AI Agent,即人工智能智能体,是指能够在特定环境中自主感知、推理、决策并执行行动以实现特定目标的智能系统[1]。自主性反应性主动性(Pro-activeness)和社会性fill:#333;important;important;fill:none;color:#333;color:#333;important;fill:none;fill:#333;height:1em;AI Agent核心特征自主性 Autonomy反应性 Reactivity主动性 Pro-activeness。

nmdbbzcl的博客 902

【QNX+Android虚拟化方案】136 - QNX 侧 Coredump 文件解析

【QNX+Android虚拟化方案】136 - QNX 侧 Coredump 文件解析

|~~~热爱生活、努力学习的小伙汁~~~| 1905

深入理解HashMap

深入剖析了HashMap的实现原理和源码

mazouri的博客 5353

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

JDK8 HashMap源码行级解析 红黑树操作 史上最全最详细图解

源码面前,了无秘密。 本文对HashMap红黑树部分的1790~2388行源码进行了详细解析。 本文将会在代码里把注释写得尽可能地全,以理解源码的各个细节,而在源码片段后则将进行提纲挈领的总结性讲解,在行文中可能穿插大量的图片以配合讲解,因为红黑树这种东西真的很需要看图来理解。本文既适合已经阅读过部分源码的同学,可以来这里查漏补缺,或解决疑惑;也适合从头开始阅读HashMap源码的同学。

anlian523的博客 8711

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

c++map实现原理_HashMap原理深入理解

hashing(哈希法)的概念散列法(Hashing)是一种将字符组成的字符串转换为固定长度(一般是更短长度)的数值或索引值的方法,称为散列法,也叫哈希法。由于通过更短的哈希值比用原始值进行数据库搜索更快,这种方法一般用来在数据库中建立索引并进行搜索,同时还用在各种解密算法中。对比:Hashtable、HashMap、TreeMapHashtable 是早期Java类库提供的一个哈希表实...

weixin_28864485的博客 3004

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

HashMap中的红黑树

转载自:http://blog.youkuaiyun.com/u011240877/article/details/53358305 张拭心读完本文你将了解到:点击查看 Java 集合框架深入理解 系列 - - 乾杯传统 HashMap 的缺点HashMap 在 JDK 18 中新增的数据结构 红黑树HashMap 中关于红黑树的三个关键参数HashMap 在 JDK 18 中新增的操作桶的树形化 treei...

qq_36523667的博客 997

看了两天HashMap源码,终于把红黑树插入平衡规则搞懂了

絮叨 学校短学期刚结束了,离学校开学还有很多天,一直呆在寝室玩游戏岂不是浪费了大好时光,于是心血来潮想看看HashMap的源码。虽然我没有经历过面试,但是java程序员都知道,HashMap是面试官必问的知识点,而我现在只停留在对HashMap的基本使用层面,因此我觉得有必要深入了解一下HashMap的底层原理。在HashMap中,我觉得最难的应该是红黑树了吧,我结合代码和画图软件研究了两天,终于总结出规律,现在分享给大家 一、红黑树的特点 下面是红黑树的5条性质,现在大家要对这些性质有印象,后面我会结合图

myboy125的博客 988

深入理解hashmap底层实现原理

HashMap是我们用于元素映射使用频率最高的数据结构,它继承自AbstractList类,并且支持一条值为null的Key和无数条value为null的数据,HashMap是线程不安全的6在多线程环境下我们通过使用Collections中的synchronizedMap使其具有线程安全的能力或者直接使ConcurrentHashMap,随着JDK的更新迭代,自jdk1.8以来,HashMap的底层数据结构已经发展为数组+链表+红黑树

m0_65431718的博客 8100
上一篇: dubbo入门
下一篇: 深入理解LinkedHashMap 源码分析
striveb
博客等级 码龄10年 242粉丝 117原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值