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

news2024/11/18 5:41:29

文章目录

  • Java中的HashMap
    • 什么是HashMap?
    • 对比其他Map中put()方法
    • HashMap中put()方法使用示例
  • HashMap中put()源码解析
    • 手绘流程图
    • 实现原理
    • 源码探究(JDK 1.8)
  • 设计put()的意义
  • 总结

Java中的HashMap

什么是HashMap?

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

对比其他Map中put()方法

可以看一下博主的博客了解HashMap、TreeMap和LinkedHashMap三者的使用:Java-数据结构(二)-Map:HashMap、TreeMap、LinkedHashMap
HashMap、TreeMap和LinkedHashMap都是Java中常用的Map实现类,它们在put方法的实现上有一些区别。

  1. HashMap的put方法:

    • HashMap使用哈希表来存储键值对,通过键的哈希码和哈希函数来确定键值对在哈希表中的存储位置。
    • 当发生哈希冲突时,HashMap使用链表或红黑树来解决冲突。
    • HashMap的put方法具有O(1)的平均时间复杂度。
  2. TreeMap的put方法:

    • TreeMap使用红黑树来存储键值对,根据键的自然顺序或自定义比较器来进行排序。
    • 在插入键值对时,TreeMap会根据键的顺序将键值对插入到相应的位置,以保持有序性。
    • TreeMap的put方法具有O(log n)的时间复杂度。
  3. LinkedHashMap的put方法:

    • LinkedHashMap继承自HashMap,底层仍然使用哈希表来存储键值对。
    • LinkedHashMap在HashMap的基础上,使用双向链表来维护键值对的插入顺序或访问顺序。
    • 在插入键值对时,LinkedHashMap会将键值对插入到链表的尾部,以保持插入顺序或访问顺序。
    • LinkedHashMap的put方法具有O(1)的平均时间复杂度。
  • HashMap的put方法使用哈希表来存储键值对,适用于需要高效存储和查找键值对的场景。
  • TreeMap的put方法使用红黑树来存储键值对,适用于需要有序存储和查找键值对的场景。
  • LinkedHashMap的put方法在HashMap的基础上,使用双向链表来维护插入顺序或访问顺序,适用于需要保持插入顺序或访问顺序的场景。

HashMap中put()方法使用示例

以下是一个简单的Java代码示例,展示了如何使用HashMap的put方法:

import java.util.HashMap;

public class HashMapExample {
    public static void main(String[] args) {
        // 创建一个HashMap对象
        HashMap<String, Integer> hashMap = new HashMap<>();

        // 使用put方法添加键值对
        hashMap.put("apple", 1);
        hashMap.put("banana", 2);
        hashMap.put("cherry", 3);

        // 使用get方法获取键对应的值
        int value = hashMap.get("banana");
        System.out.println("The value of 'banana' is: " + value);
    }
}

HashMap中put()源码解析

手绘流程图

在这里插入图片描述

实现原理

在Java中,HashMap的put方法的实现原理可以分为以下几个步骤:

1.首先,根据键的哈希值计算出键的哈希码。HashMap使用键的哈希码来确定键值对在哈希表中的存储位置。

2.接下来,通过哈希码计算出键值对在哈希表中的索引位置。HashMap使用一个称为“哈希函数”的算法来计算索引位置。常用的哈希函数是将哈希码与哈希表的容量进行取模运算,得到索引位置。

3.如果该索引位置上没有其他键值对,则直接将键值对存储在该位置上。如果该索引位置上已经存在其他键值对,则发生了“哈希冲突”。

4.当发生哈希冲突时,HashMap使用链表或红黑树来解决冲突。在JDK1.8之前,HashMap使用链表来解决冲突,即在冲突的索引位置上,将新的键值对插入到链表的尾部。而在JDK1.8及以后的版本中,当链表长度超过一定阈值时,HashMap会将链表转换为红黑树,以提高查找效率。

5.最后,将键值对成功存储在哈希表中,并返回先前关联的值(如果存在)。如果该索引位置上已经存在其他键值对,则需要遍历链表或红黑树,找到对应的键值对,并更新其值。

通过以上步骤,HashMap的put方法成功将键值对存储在哈希表中。需要注意的是,当哈希表的负载因子(即存储的键值对数量与容量的比值)超过一定阈值时,HashMap会进行扩容操作,以保持哈希表的性能。

源码探究(JDK 1.8)

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;
    // table未初始化或者长度为0,进行扩容
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    // (n - 1) & hash 确定元素存放在哪个桶中,桶为空,新生成结点放入桶中(此时,这个结点是放在数组中)
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    // 桶中已经存在元素(处理hash冲突)
    else {
        Node<K,V> e; K k;
        //快速判断第一个节点table[i]的key是否与插入的key一样,若相同就直接使用插入的值p替换掉旧的值e。
        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);
                    // 结点数量达到阈值(默认为 8 ),执行 treeifyBin 方法
                    // 这个方法会根据 HashMap 数组来决定是否转换为红黑树。
                    // 只有当数组长度大于或者等于 64 的情况下,才会执行转换红黑树操作,以减少搜索时间。否则,就是只是对数组扩容。
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    // 跳出循环
                    break;
                }
                // 判断链表中结点的key值与插入的元素的key值是否相等
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    // 相等,跳出循环
                    break;
                // 用于遍历桶中的链表,与前面的e = p.next组合,可以遍历链表
                p = e;
            }
        }
        // 表示在桶中找到key值、hash值与插入元素相等的结点
        if (e != null) {
            // 记录e的value
            V oldValue = e.value;
            // onlyIfAbsent为false或者旧值为null
            if (!onlyIfAbsent || oldValue == null)
                //用新值替换旧值
                e.value = value;
            // 访问后回调
            afterNodeAccess(e);
            // 返回旧值
            return oldValue;
        }
    }
    // 结构性修改
    ++modCount;
    // 实际大小大于阈值则扩容
    if (++size > threshold)
        resize();
    // 插入后回调
    afterNodeInsertion(evict);
    return null;
}

设计put()的意义

设计put方法的意义在于提供了一种简单而高效的方式来存储和查找键值对。HashMap的put方法具有O(1)的平均时间复杂度,即使在发生哈希冲突的情况下,通过链表或红黑树的处理,也能保持较快的查找性能。这使得HashMap成为了处理大量数据的理想选择,尤其在需要频繁进行键值对操作的场景下。

总结

本文深入探究了Java中HashMap的put方法的实现原理。通过了解其内部数据结构和算法,我们可以更好地理解和使用HashMap,在实际开发中更加高效地进行键值对的存储和查找操作。同时,我们也探讨了设计put方法的意义,以及提供了相关的Java代码示例。希望本文对读者有所帮助,让大家对HashMap的put方法有更深入的理解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/962729.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ARP欺骗

ARP欺骗定义 ARP欺骗&#xff08;英语&#xff1a;ARP spoofing&#xff09;&#xff0c;又称ARP毒化&#xff08;ARP poisoning&#xff0c;网络上多译为ARP病毒&#xff09;或ARP攻击&#xff0c;是针对以太网地址解析协议&#xff08;ARP&#xff09;的一种攻击技术&#x…

Qt —UDP通信QUdpSocket 简介 +案例

1. UDP通信概述 UDP是无连接、不可靠、面向数据报&#xff08;datagram&#xff09;的协议&#xff0c;可以应用于对可靠性要求不高的场合。与TCP通信不同&#xff0c;UDP通信无需预先建立持久的socket连接&#xff0c;UDP每次发送数据报都需要指定目标地址和端口。 QUdpSocket…

SpringCloudAlibaba Gateway(一)简单集成

SpringCloudAlibaba Gateway(一)简单集成 随着服务模块的增加&#xff0c;一定会产生多个接口地址&#xff0c;那么客户端调用多个接口只能使用多个地址&#xff0c;维护多个地址是很不方便的&#xff0c;这个时候就需要统一服务地址。同时也可以进行统一认证鉴权的需求。那么服…

vcs仿真教程(查看断言)

VCS是在linux下面用来进行仿真看波形的工具&#xff0c;类似于windows下面的modelsim以及questasim等工具&#xff0c;以及quartus、vivado仿真的操作。 1.vcs的基本指令 vcs的常见指令后缀 sim常见指令 2.使用vcs的实例 &#xff08;1&#xff09;新建文件夹&#xff1a; …

linux 开设端口

1&#xff0c;查看端口情况 firewall-cmd --list-all2&#xff0c;开设端口 firewall-cmd --zonepublic --add-port6379/tcp --permanent–zonepublic 表示作用域为公共的 –add-port6379/tcp 添加 tcp 协议的端口端口号为 6379 –permanent 永久生效&#xff0c;如果没有此参…

每日一题 1372二叉树中的最长交错路径

题目 给你一棵以 root 为根的二叉树&#xff0c;二叉树中的交错路径定义如下&#xff1a; 选择二叉树中 任意 节点和一个方向&#xff08;左或者右&#xff09;。如果前进方向为右&#xff0c;那么移动到当前节点的的右子节点&#xff0c;否则移动到它的左子节点。改变前进方…

枚举的简单介绍

目录 概念&#xff1a; 枚举的声明&#xff1a; 枚举的使用&#xff1a; 枚举的取值&#xff1a; 枚举的优点&#xff1a; #define的功能&#xff1a; 而与#define对比&#xff0c;枚举的优点有&#xff1a; 概念&#xff1a; 枚举顾名思义就是⼀⼀列举。 把可能的取值…

WireShark流量抓包详解

目录 Wireshark软件安装Wireshark 开始抓包示例Wireshakr抓包界面介绍WireShark 主要界面 wireshark过滤器表达式的规则 Wireshark软件安装 软件下载路径&#xff1a;wireshark官网。按照系统版本选择下载&#xff0c;下载完成后&#xff0c;按照软件提示一路Next安装。 Wire…

CUDA小白 - NPP(2) - Arithmetic and Logical Operations(2)

cuda小白 原始API链接 NPP GPU架构近些年也有不少的变化&#xff0c;具体的可以参考别的博主的介绍&#xff0c;都比较详细。还有一些cuda中的专有名词的含义&#xff0c;可以参考《详解CUDA的Context、Stream、Warp、SM、SP、Kernel、Block、Grid》 常见的NppStatus&#xf…

【100天精通python】Day50:python web编程_Django框架从安装到使用

目录 1 安装Django Web框架 2 创建一个Django 项目 3 数据模型 3.1 在应用程序的 models.py 文件中定义数据模 3.2 创建模型的迁移文件并应用 3.2.1 查询模型对象&#xff1a; 3.2.2 创建新模型对象&#xff1a; 3.2.3 更新模型对象&#xff1a; 3.2.4 删除模型对象&a…

JUC并发编程---Lock锁

文章目录 什么是Locksynchronized加锁和Lock加锁代码示例synchronized使用Lock加锁 公平锁和非公平锁公平锁&#xff1a;非公平锁&#xff1a;Lock和Synchronized的区别 synchronized 版的生产者和消费者Lock 版的生产者和消费者生产者和消费者出现的问题Condition精准通知和唤…

机器视觉工程师,人学习最大的能力是理解与善于运用,而不是记住能力

谁记得以前记住的元素周期表&#xff0c;谁能记得住乘法口诀。 如果我们去看一眼&#xff0c;就会迅速记起来。再加上我们小学机械般的练习题。再到我们在现实生活中经常用到。 其实我们机器视觉工程师&#xff0c;一定要去看&#xff0c;还要去练习​。实操软件&#xff0c;多…

深度学习-4-二维目标检测-YOLOv5源码测试与训练

本文采用的YOLOv5源码是ultralytics发行版3.1 YOLOv5源码测试与训练 1.Anaconda环境配置 1.1安装Anaconda Anaconda 是一个用于科学计算的 Python 发行版&#xff0c;支持 Linux, Mac, Windows, 包含了众多流行的科学计算、数据分析的 Python 包。 官方网址下载安装包&…

【SQL应知应会】索引 • Oracle版:B-树索引;位图索引;函数索引;单列与复合索引;分区索引

欢迎来到爱书不爱输的程序猿的博客, 本博客致力于知识分享&#xff0c;与更多的人进行学习交流 本文免费学习&#xff0c;自发文起3天后&#xff0c;会收录于SQL应知应会专栏,本专栏主要用于记录对于数据库的一些学习&#xff0c;有基础也有进阶&#xff0c;有MySQL也有Oracle …

面试被打脸,数据结构底层都不知道么--回去等通知吧

数据结构之常见的8种数据结构&#xff1a; -数组Array -链表 Linked List -堆 heap -栈 stack -队列 Queue -树 Tree -散列表 Hash -图 Graph 数据结构-链表篇 Linklist定义&#xff1a; -是一种线性表&#xff0c;并不会按线性的顺序存储数据&#xff0c;即逻辑上相邻…

解码自我注意的魔力:深入了解其直觉和机制

一、说明 自我注意机制是现代机器学习模型中的关键组成部分&#xff0c;尤其是在处理顺序数据时。这篇博文旨在提供这种机制的详细概述&#xff0c;解释它是如何工作的&#xff0c;它的优点&#xff0c;以及它背后的数学原理。我们还将讨论它在变压器模型中的实现和多头注意力的…

设计模式-10--多例模式(Multition pattern)

一、什么是多例模式&#xff08;Multition pattern&#xff09; 多例模式&#xff08;Multition pattern&#xff09;是单例模式的一种扩展&#xff0c;它属于对象创建类型的设计模式。在多例模式中&#xff0c;一个类可以有多个实例&#xff0c;并且这些实例都是该类本身。因…

实现不同局域网间的文件共享和端口映射,使用Python自带的HTTP服务

文章目录 1. 前言2. 本地文件服务器搭建2.1 python的安装和设置2.2 cpolar的安装和注册 3. 本地文件服务器的发布3.1 Cpolar云端设置3.2 Cpolar本地设置 4. 公网访问测试5. 结语 1. 前言 数据共享作为和连接作为互联网的基础应用&#xff0c;不仅在商业和办公场景有广泛的应用…

设计模式-5--适配器模式(Adapter Pattern)

一、什么是适配器模式&#xff08;Adapter Pattern&#xff09; 适配器模式&#xff08;Adapter Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许将一个类的接口转换成客户端所期望的另一个接口。适配器模式主要用于解决不兼容接口之间的问题&#xff0c;使得原本…

Windows安装jdk

Windows安装jdk 小白教程&#xff0c;一看就会&#xff0c;一做就成。 1.准备安装包&#xff08;需要的滴滴我&#xff09; 2.安装 我是在d盘创建jdk目录&#xff0c;把jdk包解压到jdk里 计算机右键---属性---高级系统设置—环境变量 &#xff08;系统变量里&#xff09;--新…