常用集合之HashMap源码浅析

【Java集合源码剖析】HashMap源码剖析 HashMap简介 HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长。 HashMap是非线程安全的,只是用于单线程环境下,多线程环境下可以采用concurrent并发包下的concurrentHashMap。 HashMap 实现了Serializable接口,因此它支持序列化,实现了Cloneable接口,能被克隆。 阅读详情

类图

源码

构造方法

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {
    //无参构造函数时使用该 负载因子
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
    //最大容量十进制值为 1073741824  
    static final int MAXIMUM_CAPACITY = 1 << 30;
    //The next size value at which to resize (容量capacity * 负载因子loadfactor)
    int threshold;

    //保存数据的数组
    transient Node<K,V>[] table;

    public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial 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);
    }

    public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }

    public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }



}

数据结构

如下是节点的数据结构,由源码可知是一个单向链表:

 static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next; //下一个节点,即子节点

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

        //...     
    }

由Node数据结构和数据源table可知HashMap是一个数组,而数组的每一个节点,又可以分支为一个一个的单向链表。由此可以得到HashMap数据结构的以下模型:

put方法

···

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

 final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
        Node<K,V> e; K k;
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    ++modCount;
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}

···
可见,经过了一顿复杂的算法。总结起来就像是下面这样:

首先会将传入的 Key 做hash运算计算出 hashcode,然后根据数组长度取模计算出在数组中的index下标。
由于在计算中位运算比取模运算效率高的多,所以 HashMap规定数组的长度为2^n。这样用 2^n - 1 做位运算与取模效果一致,并且效率还要高出许多。
由于数组的长度有限,所以难免会出现不同的Key通过运算得到的index相同,这种情况可以利用链表来解决,HashMap 会在table[index]处形成链表,采用头插法将数据插入到链表中。

get方法

get和put类似,也是将传入的Key 计算出 index ,如果该位置上是一个链表就需要遍历整个链表,通过 key.equals(k) 来找到对应的元素。就不细说了。

遍历方式

 Iterator<Map.Entry<String, Integer>> entryIterator = map.entrySet().iterator();
 while (entryIterator.hasNext()) {
     Map.Entry<String, Integer> next = entryIterator.next();
     System.out.println("key=" + next.getKey() + " value=" + next.getValue());
 }
Iterator<String> iterator = map.keySet().iterator();
while (iterator.hasNext()){
    String key = iterator.next();
    System.out.println("key=" + key + " value=" + map.get(key));
}
map.forEach((key,value)->{
    System.out.println("key=" + key + " value=" + value);
});

强烈建议使用第一种 EntrySet 进行遍历。

第一种可以把 key value 同时取出,第二种还得需要通过key 取一次 value,效率较低, 第三种需要 JDK1.8以上,通过外层遍历 table,内层遍历链表或红黑树。

ps: 文末推荐其他几篇有关集合的文章:

Java集合源码笔记:HashMap+List集合 大型分布式系统犹如一个生命,系统中各个服务犹如骨骼,其中的数据犹如血液,而Kafka犹如经络,串联整个系统。这份Kafka源码笔记通过大量的设计图展示、代码分析、示例分享,把Kafka的实现脉络展示在读者面前,帮助读者更好地研读Kafka代码。麻烦帮忙转发一下这篇文章+关注我《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!量的设计图展示、代码分析、示例分享,把Kafka的实现脉络展示在读者面前,帮助读者更好地研读Kafka代码。 阅读详情

相关推荐

常用的集合有哪些

1) ArrayList 实现了 List 接口,继承了 AbstractList 类,由一个 Object[] 实例实现,即底层为数组结构;2) 默认初始化长度为 16,扩容加载因子为 0.75,一旦大于 0.75*16之后,就会调用resize()进行扩容,扩容2倍,即32;2) 默认初始化长度为 16,扩容加载因子为 0.75,一旦大于 0.75*16之后,就会调用resize()进行扩容,扩容2倍,即32;在实现的方法上,用 synchronized 关键字进行了修饰,即在方法上实现了同步锁。

zhangjiaming_zjm的博客 1268

面试必考之HashMap源码分析与实现

以下是JDK1.8之前版本的源码简介 一、什么是HashMap? Hash:散列将一个任意的长度通过某种(hash函数算法)算法转换成一个固定值。 Map:存储的集合、类似于地图X,Y坐标的存储 总结:通过hash出来的值,然后通过这个值定位到这个map,然后把这个value存储到这个map中 ~~ hashMap基本原理 二、HashMap形式? key,value。例如 w...

xiaozhegaa的博客 2565

安防相机宽动态(WDR)技术解析:从原理到实战应用

本文深入解析安防相机宽动态(WDR)技术原理,对比多次曝光与DOL-WDR等实现方式,并区分硬件WDR与数字WDR(DWDR)的本质差异。文章重点阐述了WDR在逆光出入口、夜间道路监控等五大典型场景的实战应用与调参技巧,旨在帮助用户根据实际需求正确选择与配置,确保监控系统在复杂光线下清晰成像。

weixin_29191081的博客 427

第一章 JAVA集合之HashMap源码浅析

屌丝程序员的奋斗之路现在开始                java集合这一块无论在面试或在写代码中,我们都会接触到,所以java集合是特别重要的,其中HashMap更是被我们经常用到。                一.概括                HashMap是用键值对的既已key-value的形式来存储值的,当然这只是展现给大家的一种表象,key和value都可以为空,但是ke

屌丝程序员的奋斗之路 864

我用 Claude Code 给 Spring Boot 老项目做了一次代码 Review:查出 4 类隐藏问题

接手 Spring Boot 老项目时,真正麻烦的往往不是编译报错,而是分散在业务代码中的空指针、N+1 查询、异常吞没和并发隐患。本文记录一次使用 Claude Code 辅助代码 Review 的完整流程,并给出提示词、Java 示例、修改方案和人工复核方法。

海波的专栏 396

麒麟V10服务器多硬盘安装与分区实战:告别自动分区,手动分配/boot、swap和/根目录

本文详细介绍了麒麟V10服务器在多硬盘环境下的手动分区策略,包括/boot、swap和/根目录的优化分配方案。通过SSD与HDD混合存储架构的合理规划,提升系统性能和运维效率,适用于企业级定制化系统部署需求。

weixin_26976635的博客 405

对于Hashmap源码的理解

最近读了hashmap的源码,以下简单谈谈我对于hashmap源码的理解。 java标准库中hashmap就是基于拉链法,底层是一个数组,数组每一项又是一个链表。 拉链法解释:拉链法是解决哈希冲突的一种行之有效的方法,某些哈希地址可以被多个关键字值共享,这样可以针对每个哈希地址建立一个单链表。在拉链(单链表)的哈希表中搜索一个记录是容易的,首先计算哈希地址,然后搜索该地址的单链表。 效果图如...

不xiao益君的专栏 468

S32DS3.5图形化配置实战:从零构建S32K144通信接口工程

本文详细介绍了如何使用S32DS3.5图形化配置工具从零构建S32K144通信接口工程。通过实战演示引脚配置、时钟树设置、SPI接口搭建等关键步骤,帮助开发者快速掌握NXP S32K系列开发技巧,提升嵌入式开发效率。特别针对S32DS3.5版本的新特性进行了深度解析,包括智能代码生成和调试优化方法。

weixin_26834815的博客 199

HashMap源码解析

1. 底层数据结构 2. 常用 API 2.1 添加元素 2.2 删除元素 2.3 查找元素

FireFox1997 432

3D点云目标检测Complex-YOLO(训练篇)(一)———KITTI数据集预处理与制作

KITTI dataset Download dataset KITTI 3D Object Detection Evaluation 2017 link 下载四个部分,共41.4GB 解压后为四部分内容(相机校准矩阵calib、RGB图像image_2、标签label_2、点云数据velodyne) 对应的testing和training数据。其中,training数据为7481张(图片和点云对应的场景),testing数据 7518张(无label_2数据)。 Data Preprocess

“知识最大的敌人不是无知,而是错觉” 7149

(第14天)Veritas NetBackup(NBU) 配合 RMAN 脚本备份 Oracle 数据库

NBU(Veritas Netbackup)是企业中比较常用的备份软件,通常用于备份企业使用中的系统,数据库,文件等等,通常是搭配 Oracle RMAN 来进行备份 Oracle 数据库。

LuciferLiu_DBA 1641

hashMap底层源码浅析

hashmap是我们经常使用的一个工具类。那么知道它的一些原理和特性吗? 特性 HashMap是一种基于散列算法实现的快速查找的键值对结构。底层实现是链表数组。 允许空键和空值(但空键只有一个,且放在第一位) 元素是无序的(这里的无序是指的插入和读取的顺序不一致) JDK 8 后又加了底层加上了红黑树优化过长的链表以及并行遍历。 概述 HashMap可以分析的地方很多,网上也有许多文章,本文仅从以下几个方面进行分析: 基础变量 插入(动态扩容,延迟插入,红黑树转换,可以说的地方很多) 并行遍历(jdk

qq_35824590的博客 1万+

SAP SD销售订单的ATP检查简介

如果可用性检查组设置为01(汇总需求),系统将更新销售订单需求到汇总表VBBS,该表中未记录销售订单号码,因此在MD04中不会显示销售订单号码,而是按天汇总显示销售需求。如果物料的可用性检查组设置为02(单个需求),系统将更新销售订单需求到表VBBE,该表中记录了销售订单号码,因此在MD04显示销售订单的号码以及需求,前面的文章中我们解释了PP模块中的ATP检查,也解释了MM模块中的ATP的检查,本文将说明一下SD模块中的ATP检查。可用性检查用的最多的就是02,有兴趣的同学可以测试一下可用性检查01。

我小时候很黑的博客 4291

Java集合之HashMap源码详解

HashMap的源码详细解析

个人博客,欢迎收藏。 1930

深入URP之Shader篇1: URP Shader概述

我们知道,URP和HDRP都是基于SRP的,尽管现在可以使用Shader Graph编写Shader,但是手写Shader仍然是有一定的优势,特别是在性能方面,手写Shader可以更极致的优化,因此我们在项目中前期都是直接使用Shader Graph编写Shader,项目后期会改成手写Shader来提高性能。而在SRP中手写Shader,我们仍然使用的是Shader Lab,这个结构并没有太大的变化,但是SRP Shader中使用Shader Lab语言还是有几点不同。

聚焦游戏和图形引擎开发技术 4637

OV9734 DATASHEET,完整规格书

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

Map集合类——HashMap

Map集合 Map集合存储的是键值对 Map集合的实现类:HashTable、LinkedHashMap、HashMap、TreeMap 1、HashMap 1.1、基础了解: 1、键不可以重复,值可以重复; 2、底层使用哈希表实现; 3、线程不安全; 4、允许key为null,但只允许有一条记录为null,value也可以为null,允许多条记录为null; 1.2、源码分析 (一)以...

代码学习之路的博客 9614

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

HashMap集合源码详解

HashMap是Map接口的实现类,基于哈希表结构实现的。其主要特点是以key-value存储形式存储数 据,即用与存放键值对。HashMap 的操作不是同步的,这意味着它线程不安全。特点:无序性 : 存入和取出元素顺序不一致唯一性 : 键key是唯一的可存null : 键和值位置都可以是null,但是键位置只能是一个null数据结构 : 数据结构控制的是键key而非值value!jdk1.8之前数据结构是:链表 + 数组jdk1.8之后数据结构是:链表 + 数组 + 红黑树。

哀莫大于心死 1082

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Java 集合框架 源码浅析 与理解

Java集合框架 collection map set hashmap hashset queue

永远不要停下学习的脚步。 7024

Java集合框架源码分析:hashMap

特性描述是否允许为nullkey和value都可以取值为null是否允许重复key重复会覆盖,value允许重复是否有序无序是否线程安全非线程安全JDK1.8之前HashMap是由数组 + 链表结构组成,数组是HashMap的主体,链表则是主要为了解决哈希冲突(两个对象调用的hashCode方法计算的哈希码值一致导致计算的数组索引值相同)而存在(拉链法:解决冲突)。JDK1.8以后(包含JDK1.8)HashMap是由数组+链表+红黑树组成。

玉汝于成 1521

Java集合之HashMap(含源码解析 超详细)

上文讲解了ArrayList,这次我们来看HashMapHashMap 主要用来存放键值对,它基于哈希表的Map接口实现,是常用的Java集合之一。

rc1596919047的博客 1454

android 常用集合HashMap的使用和详解

Java的集合类由Collection接口和Map接口派生 集合类的接口定义 1) Collection 一组"对立"的元素,每个位置只能保存一个元素(对象),通常这些元素都服从某种规则   1.1) List必须保持元素特定的顺序   1.2) Set不能有重复元素   1.3) Queue保持一个队列(先进先出)的顺序 2) Map 一组成对的"键值对"对象,就像一个小型数据库。我们...

cheweilol的专栏 6687

java集合及源码

数组在内存存储方面的特点– 数组初始化以后,长度就确定了。数组中的添加的元素是依次紧密排列的,有序的,可以重复的。– 数组声明的类型,就决定了进行元素初始化时的类型。不是此类型的变 量,就不能添加。arr[0] = 1;//编译报错– 可以存储基本数据类型值,也可以存储引用数据类型的变量• 数组在存储数据方面的弊端:– 数组初始化以后,长度就不可变了,不便于扩展– 数组中提供的属性和方法少,不便于进行添加、删除、插入、获取元素个数等操作,且效率不高。

曦 1370

Java集合——HashMap源码浅析

一、继承关系 public class HashMap<K,V> extends AbstractMap<K,V>//继承自AbstractMap父类 implements Map<K,V>, Cloneable, Serializable//实现Map接口、克隆、序列化 二、属性及默认值 ////默认容量(哈希结构中数组默认大小)为16 st...

weixin_42738587的博客 852

HashMap是如何实现键值对管理的?从底层源码入手,详细解读HashMap的具体实现

本文首先对集合、映射等相关知识进行介绍,对HashMap各个重要变量、常量、构造方法、核心方法、序列化原理等源码进行深入解析,并对:“Map是不是集合?”、“HashMap继承了AbstractMap,为什么还要实现Map接口?”、“为什么table用transient修饰?”、“为什么(e.hash & oldCap) == 0时扩容索引值不变?”等问题进行了分析和解答

种一棵树最好的时间是十年前,其次是现在 1129

Java集合篇之HashMap解析与源码分析

HashMap是Map接口的一个实现类,它基于哈希表实现,允许存储键值对。HashMap通过哈希函数将键映射到哈希表中的某个位置,从而实现快速的查找、插入和删除操作。

庄子兄弟的博客 1899

Java常用集合之HashMap(深入源码)

Java常用集合之HashMap(深入源码)

m0_62609939的博客 904
上一篇: 常用集合之LinkedList源码浅析
下一篇: 常用集合之HashSet源码浅析
She_lock
博客等级 码龄12年 157粉丝 134原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值