Java HashMap的put过程

JDK1.8,Java,HashMap的put()方法全过程(超详细) 7,产生Hash冲突,且和首节点不相同,且链表没有树化,那么我们需要遍历这个链表,判断键是否相同,如果键没有匹配的,就插入到链表尾部,插入后,判断长度是否超过8,超过就进行树化(里面还会对数组长度进行判断,如果没有超过64,就进行扩容)8,产生Hash冲突,且和首节点不相同,且链表没有树化,那么我们需要遍历这个链表,判断键是否相同,如果找到了匹配的键,就退出死循环,会自动执行最后面的,Key相同把Value值进行替换的代码。5,key相同则直接走线面的步骤,进行Value值的替换。使用内部类Node数组。 阅读详情

1. 通过源码来分析HashMap的put过程

Object key = new Object();
Object value = new Object();
Map<Object, Object> map = new HashMap<Object, Object>();
map.put(key, value);

当我们执行上面这段代码时,HashMap的底层是怎么的一个实现过程呢,下面我们就来一一揭晓。
首先程序会执行HashMap里的put方法就是下面的这个代码啦。

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

从这个put方法的代码可以看出首先调用hash方法,这个方法时干嘛的呢?实际就是计算这个key的hash值,hash方法的代码如下,经过这个操作之后就获取到了key的hash值。

static final int hash(Object key) {
	int h;
	return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

然后就调用了putVal这个方法,这个方法就是核心内容啦,面试的经过问的就是这里啦(重点),下面仔细看啦!!

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;
}

首先这里先判断一下这个存放元素的数组是不是null(我这里分析的是JDK1.8的代码,在jdk1.8中new HashMap的时候不会先创建这个数组,而是在第一次添加元素的时候创建)。

if ((tab = table) == null || (n = tab.length) == 0)
	n = (tab = resize()).length;

就是这个if 判断,如果数组是空,就调用 resize方法去new出这个数组并且大小为16(默认初始大小)。然后就去计算当前我们添加的这个元素在数组中的索引,代码如下。

if ((p = tab[i = (n - 1) & hash]) == null)
	tab[i] = newNode(hash, key, value, null);

代码中的 i就是当前的这个元素应该在数组中的索引,(p = tab[i = (n - 1) & hash]) == null,p表示当前的这个位置储存的元素。说明现在已经出现了hash冲突,下面就是解决hash冲突的方法。

2. 解决Hash冲突

  1. 如果 p == null就表示当前这个位置没有存储任何元素,就把我们当前新添加的 生产一个新的 Node 元素,并把这个新的Node元素存放在数组中 i 的位置。 newNode方法如下。
    Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) {
       return new Node<>(hash, key, value, next);
    }
    
  2. 否则(也就是 p != null)就是数组i的位置存有元素,就进行下面的操作。
    Node<K,V> e; K k;
    if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
        e = p;
    
    首先比较p.hash == hash && ((k = p.key) == key数组i位置的元素p的hash等于我们新添加的元素key的hash(这个p是一个Node类型的,p的hash实际就是p的key的hash)并且这个元素p的key==我们新添加元素的key或者key != null && key.equals(k)))就表明我们添加的元素的key就是数组i位置p元素的key。后面就会更新这个p元素的value为我们新添加元素的value。 p.hash == hash && ((k = p.key) == key这个判断是为了比较当key是基本数据类型的时使用的。key != null && key.equals(k)))是为了当key为引用数据类型时使用的。
  3. 如果 元素p既不等于null,并且p的key也不是我们新添加的元素的key,这时就就需要进行下面的操作啦,就要把我们新添加的元素添加到数组i位置下组成的链表里。
    for (int binCount = 0; ; ++binCount) {
       if ((e = p.next) == null) { // 如果p的后一个是null就把我们新添加的元素添加到这个链表的末尾
           p.next = newNode(hash, key, value, null);
           if (binCount >= TREEIFY_THRESHOLD - 1) // 判断这个链表的长度是不是大于阈值,大于就把链表转成红黑树
               treeifyBin(tab, hash);
           break;
       }
       //执行和上面解决hash冲突的第二步一样的判断和操作。
       if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
           break; //说明这个链表中存在元素的key和我们新添加的key是相等的,
       p = e;
    }
    
  4. 最后判断 e 是不是null,如果不是说明这个map中存在我们新添加的key,然后就执行下面的程序判断是不是要覆盖掉原来的value值。
    if (e != null) { // existing mapping for key
        V oldValue = e.value;
        if (!onlyIfAbsent || oldValue == null) // onlyIfAbsent 表示当原来的值为null的时候才替换我们添加的值
            e.value = value;
        // 执行后序操作,如果使用的是LinkedHashMap的话会去维护链表的结构。LinkedHashMap继承了HashMap
        afterNodeAccess(e); 
        return oldValue;
    }
    

最后执行下面的程序,表示我们新添加的元素,在map中没有相同的key,正常的添加到了map中,之后执行一些后序的操作

++modCount;
if (++size > threshold)
    resize();
afterNodeInsertion(evict);
return null;

3.最后总结

Object key = new Object();
Object value = new Object();
Map<Object, Object> map = new HashMap<Object, Object>();
我们调用 map.put(key, value)方法时

  1. 计算key的hash值 ,hash = (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
  2. 通过一定的方法计算 key 在数组中的索引 i ,i = (n - 1) & hash]
  3. 判断数组中索引为i的位置是否已经有值,if ((p = tab[i = (n - 1) & hash]) == null)
    • 如果没有值,就 创建一个新的 Node,并且把这个新的Node存到这个数组索引为i的位置,tab[i] = newNode(hash, key, value, null);
    • 如果有值,就在当前元素组成的链表上搜索是否存在已经存在的Node的key我们新添加的key相同的。
      • 如果在当前的这个链表上存在Node的key和我们新添加的key相同,就把我们新添加的value覆盖掉这个Node的value。(实际上最后执行putVal方法时有个参数onlyIfAbsent可以控制是否覆盖)
      • 如果在当前的这个链表上没有Node的key和我们新添加的key相同,就把我们新添加的key和value组成一个新的Node添加到这个链表的最后面 (JDK1.7之前都是插在这个链表的最前面就是数组i的位置,这样就会存在线程安全的问题容易发生链表的环形结构。JDK1.8之后插到这个链表的最后面,这样也不是线程安全的,因为在1.8中的put主函数中如下,如果没有hash碰撞则会直接插入元素。如果线程A和线程B同时进行put操作,刚好这两条不同的数据hash值一样,并且该位置数据为null,所以这线程A、B都会进入第6行代码中,造成先插入的数据被覆盖掉)。
        if ((p = tab[i = (n - 1) & hash]) == null)
                tab[i] = newNode(hash, key, value, null);
        
深信服AF设置安全防护策略 防火墙设置安全防护策略,禁止用户访问指定网站 阅读详情

相关推荐

Qt 5.12.8在Linux下编译qtvirtualkeyboard模块,我踩过的那些坑(附完整解决方案)

本文详细介绍了在Linux系统下为Qt 5.12.8单独编译qtvirtualkeyboard模块的完整解决方案。通过分析模块缺失原因、提供精准的编译步骤和常见问题处理,帮助开发者高效解决虚拟键盘模块缺失问题,避免重新编译整个Qt源码的繁琐过程。

weixin_29248611的博客 457

java put 语句_Java put语句

匿名用户1级2017-02-20 回答put(K, V) - 类 java.util.AbstractMap 中的方法将指定的值与此映射中的指定键关联起来(可选操作)。put(K, V) - 类 java.util.concurrent.ConcurrentHashMap 中的方法将指定 key 映射到此表中的指定 value。put(K, V) - 类 java.util.Dictionary ...

weixin_34397537的博客 5264

(一)利用Python实现数据集读取,Python基本文件操作介绍

机器学习是计算机对大量数据的分析过程,在机器学习的实际项目中,通常都需要准备数据集,数据集通常包含的数据数量是庞大的。例如,在处理计算机视觉领域的项目时,每一张图片都是一个单独的文件,为了提高模型训练的效果,图片数量通常会比较多,因此需要合理且高效的操作将所有的图片数据读取。本篇旨在介绍如何用Python对海量文件进行遍历,也是机器学习实践的第一步。.........

ZZR2631574030的博客 6355

介绍java的put

在Java中,put方法通常与映射(Maps)和属性列表(Properties)等数据结构相关联。这个方法主要用于在映射或属性列表中添加或修改键值对。以下是对put。

m0_57236802的博客 1741

STM32F0实现数字化SPWM纯正弦波逆变器

这个没啥好说的,搭电路的事。其核心思想是两边桥臂分别由两个180度相位差的正弦波与三角载波比较来控制,优点是在开关器件工作频率一样的情况下,输出PWM频率是单极性SPWM的2倍,能够明显减少谐波,因此只需要相对较小的电感和电容滤波器件就可以起到很好的滤波效果,提升了系统的性能,也精简了系统体积,降低了成本。而双极性pwm则是在整个正弦周期内,四只管子都在高频开关状态工作,对角线上的管子一起导通,另一对角线上管子互补导通,实际上只需要一路互补的PWM控制,负载上电压在正电压和负电压之间切换,不出现0v。

forlifefor的专栏 1万+

工业级以太网通信实战:用FPGA驱动YT8531 PHY芯片的完整避坑指南

本文是一份针对工业级以太网通信的实战指南,详细讲解了如何使用FPGA驱动YT8531 PHY芯片。内容涵盖从硬件设计(电源、时钟、复位)到MDIO接口驱动开发、PHY配置与链路管理的全流程,并重点剖析了十个常见的开发“陷阱”及其解决方案,旨在帮助工程师在复杂的工业环境中实现稳定可靠的以太网通信连接。

weixin_29191669的博客 141

Hashmap 的源码解析以及put流程

一.Hashmap的定义 Hashmap是一种常用的数据结构,底层是基于数组和链表实现的。 二.Hashmap的详情结构 2.1 数据结构 HashMap提供了三个构造函数: HashMap():构造一个具有默认初始容量 (16) 和默认加载因子 (0.75) 的空 HashMap。 HashMap(int initialCapacity):构造一个带指定初始容...

健康平安的活着的专栏 4911

深入NAND Flash信号与接口:结合ONFI 5.0协议,聊聊DBI、ODT和差分信号到底在解决什么实际问题

本文深入探讨了NAND Flash信号与接口设计,结合ONFI 5.0协议,详细解析了DBI、ODT和差分信号等技术如何解决高速数据传输中的信号完整性问题。通过实际案例和数据,展示了这些技术在降低功耗、减少噪声和提升抗干扰能力方面的显著效果,为存储工程师提供了宝贵的实践参考。

weixin_33736649的博客 341

HashMap put方法的具体流程

HashMap是 Java 中基于哈希表实现的键值对(key-value)存储容器。它允许快速的插入、删除和查询操作,且允许键和值为null(但键不能重复)。HashMap是非线程安全的,适用于单线程环境。HashMap的put核心机制:哈希计算和链表/红黑树存储。冲突处理:链表与红黑树相结合,优化性能。动态扩容:保证较低的冲突率和高效访问。

Flying_Fish_roe的博客 1641

UE5抗锯齿实战:如何用TAA解决闪烁问题(附参数优化指南)

本文深入探讨了UE5中TAA(时间性抗锯齿)技术如何有效解决画面闪烁问题,并提供了详细的参数优化指南。通过基础参数配置、高级优化技巧以及材质层面的策略,帮助开发者显著提升视觉质量。文章还介绍了诊断工具链,助力快速定位并解决顽固闪烁问题。

solidity8miner的博客 423

SpringBoot从入门到精通(全)

前言 ==本博文结合软件idea进行主要讲解== **springboot的特性主要有** - 简化 Spring 应用程序的创建和开发过程 - 抛弃了繁琐的 xml 配置过程,采用大量的默认配置简化以及注解反射 - 直接使用 java main 方法启动内嵌的 Tomcat 服务器运行 Spring Boot 程序,不需要部署 war 包文件 **四大核心**分别为自动配置、起步依赖、Actuator和命令行界面

码农研究僧的博客 7万+

Java-HashMap中put()方法是如何实现的,内含详细流程图

HashMap是Java中常用的数据结构之一,它提供了快速的键值对存取能力。本文将深入探究HashMap的put方法的实现原理,解析其内部数据结构和算法,并探讨设计put方法的意义。

向诸位大佬虚心学习 7902

ESP32 智能垃圾分类助手:从硬件搭建到代码实现的硬核指南

基于 ESP32 的智能垃圾分类设备,充分利用了 ESP32 的外设兼容性和处理能力,通过简单的模块组合和代码编写,就能实现垃圾分类的智能化。无论是作为学生的物联网课程设计,还是家庭的环保小工具,这个项目都兼具实用性和趣味性。如果你想进一步优化功能,还可以尝试加入更多传感器(如红外传感器检测垃圾满溢),让设备更加智能。

weixin_42300449的博客 928

hashmap put过程_Java 面试经典之 HashMap 原理

介绍在你的工作面试中,这个问题被问了多少次?对我来说超过 10 次,我想把这个问题说得非常清楚,以节省每个人的时间。HashMap 如何在 Java 中工作HashMap 是一个基于散列表的键值对容器。它通常充当一个二进制哈希表,但是当 bins 变得太大时,它们就会被转换成 TreeNodes 的 bins。一个典型的 HashMap 看起来是这样的:哈希表该表充当索引,在第一次使用时初始化,例...

weixin_39553757的博客 786

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

java——对HashMap的put过程的简述

HashMap解析put的过程 首先,用代码运行下,来体会下: 代码实现: @Test public void test1() { //创建了一个HashMap Map<String,Object>map = new HashMap<>(); //使用put方法保存数据 map.put("age", 12); map.put("name", "gaga"); System.out.println(map); } 运行结果: 首先经过了hash

jxcncb的博客 4107

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

Connor学Java - HashMap put方法执行过程

Hey,欢迎阅读Connor学Java系列,这个系列记录了我的Java基础知识学习、复盘过程,欢迎各位大佬阅读斧正!原创不易,转载请注明出处:http://t.csdn.cn/ojmcU,话不多说我们马上开始!

ConnorYan的博客 263

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

【Java】HashMap的put过程

如果桶为空就直接插入;如果桶中已经有元素,就判断 key 是否相同,相同则覆盖 value,不同则处理哈希冲突。插入后,如果链表节点数量达到 8,并且数组容量至少为 64,就把链表转换成红黑树;扩容时容量通常翻倍,新索引只比旧索引多判断一位,所以元素的新位置要么还是原位置,要么是原位置加上旧容量。HashMap 会在 resize 时把节点迁移到新数组,之后 get 使用新容量重新计算下标,因此仍然能够找到原来的元素。新节点插入链表后,如果当前桶中的节点数量达到 8,会尝试把链表转换成红黑树。

weixin_66829320的博客 214

java8 HashMap put过程 源码分析

public V put(K key, V value) { return putVal(hash(key), key, value, false, true); } 很明显是putVal方法来完成的,看看怎么说。 final V putVal(int hash, K key, V value, boolean onlyIfAbsent, ...

Handoking的博客【学无止境】 523

Java HashMap执行put方法的过程详解

5、如果不是treenode,开始遍历链表,判断链表长度是否大于8,如果大于8就转成红黑树,在树中执行插入操作,如果不是大于8,就在链表中执行插入;HashMap的数据结构在jdk1.8之前是数组+链表,为了解决数据量过大、链表过长是查询效率会降低的问题变成了数组+链表+红黑树的结构,利用的是红黑树自平衡的特点。3、判断table[i]==null,如果是true,直接新建节点进行添加,如果是false,判断table[i]的首个元素是否和key一样,一样就直接覆盖。

优秀技术文章收录 545

Java8 HashMap put( ) 过程解析

put( ) 方法用于向 HashMap 中插入一个键值对,如果键已存在,那么就替换原来的值,如果键不存在,那么就创建一个新的节点并插入到 HashMap 中。

weixin_46623496的博客 750

hashmap put过程_面试题:Java容器之HashMap

点击上方“程序员面试圈”,选择“置顶或者星标”与你一起成长~公众号回复[资源]领取888G学习资源HashMap 概述Map 是 Key-Value 对映射的抽象接口,该映射不包括重复的键,即一个键对应一个值。HashMap 是 Java Collection Framework 的重要成员,也是Map族(如下图所示)中我们最为常用的一种。简单地说,HashMap 是基于哈希表的 ...

weixin_39649611的博客 812

hashmap put过程_【Java集合框架002】原理层面:HashMap全解析

一、前言二、HashMap2.1 HashMap数据结构 + HashMap线程不安全 + 哈希冲突2.1.1 HashMap数据结构学习的时候,先整体后细节,HashMap整体结构是 底层数组+链表 ,先记住,再开始看下面的HashMap相关知识点:底层数据结构:HashMap基于哈希散列表实现 ,可以实现对数据的读写。插入逻辑put()方法:将键值对传递给put方法时,它调用键对象的...

weixin_39895881的博客 143

HashMap中put的过程(Java)

HashMap中put的过程(Java)

胖胖的博客 355

JDK1.8,Java,HashMap的put()方法全过程

首先一个节点进来做hash值并取余运算,hash到对应的桶,开有没有hash冲突,如果没有直接插入,如果有就需要遍历链表,看有没有key相同的情况,如果有就进行值的替换,如果没有就插到链表尾部(哪些判断是否变为红黑树逻辑在下面进行解析,这里做个大概描述)...

guanshengg的博客 235

Java集合——Map集合的继承结构、遍历方法、HashMap的底层结构,put方法的执行过程

本篇文章介绍了Map集合的继承结构,Map接口的常用方法和遍历方法,HashMap的底层结构和put方法的执行过程。参考视频:【Java零基础视频教程中部,java入门必刷视频动力节点老杜javase】 https://www.bilibili.com/video/BV1Eb4y1P7iq/?p=165&share_source=copy_web&vd_source=17d73b72d648e6763cecfd48ea369ec6

weixin_46750485的博客 2209

Java集合面试题:了解Hashmap吗,说一说put的过程。

摘要:HashMap是Map接口的实现类,采用键值对存储结构。其底层实现由数组+链表/红黑树组成:JDK1.7为数组+链表并使用头插法,JDK1.8改进为数组+链表+红黑树,并将插入方式改为尾插法。这种结构优化了哈希冲突处理,提升了查询效率和线程安全性。

n1449211219的博客 140
上一篇: Java中的代理模式和装饰者模式
下一篇: Spring学习笔记第三天,AOP的相关概念、Spring中的AOP、Spring中基于XML配置的AOP和基于注解配置的AOP
Code_Boy_Code
博客等级 码龄9年 650粉丝 28原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值