hashmap的存放元素的实现过程

2023-11-04

1.代码实现

 // 存放运算的方法,hash(key)即获取key的hash码值,算法为(key.hashcode())^(key.hashcode()>>>16),前面有分析
  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)
    	// resize()扩容过程前面有分析
        n = (tab = resize()).length;
    // 用hash和数组长度减一做与运算确定元素插入位置,如果该位置没有元素则创建一个结点放在那里
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
    	// 如果有元素则走此
        Node<K,V> e; K k;
        // 判断此位置元素的hash值与键值key是否相等,相等则替换value
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        else if (p instanceof TreeNode)
        	// 判断p是否是红黑树结点,是将该值插入到红黑树中
            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,达到则进行树化
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                    	// 树化实现过程,前面有分析
                        treeifyBin(tab, hash);
                    break;
                }
                // 在遍历链表的过程中发现hash和key 相同则直接替换掉该值
                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;
            // onlyIfAbsent 如果存在该值是否替换,false为替换,true表示不替换,默认为false
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            // 该方法空实现,不用管
            afterNodeAccess(e);
            return oldValue;
        }
    }
    // modCount记录的是修改次数,不用管
    ++modCount;
    // 如果数组使用大于阀值,扩容
    if (++size > threshold)
    	// 扩容方法
        resize();
     // 空实现,不用管
    afterNodeInsertion(evict);
    return null;
}

// 该方法可以参考前面分析的treeify方法,实现过程基本一样
 final TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab,
                                int h, K k, V v) {
     Class<?> kc = null;
     boolean searched = false;
     TreeNode<K,V> root = (parent != null) ? root() : this;
     for (TreeNode<K,V> p = root;;) {
         int dir, ph; K pk;
         if ((ph = p.hash) > h)
             dir = -1;
         else if (ph < h)
             dir = 1;
         else if ((pk = p.key) == k || (k != null && k.equals(pk)))
             return p;
         else if ((kc == null &&
                   (kc = comparableClassFor(k)) == null) ||
                  (dir = compareComparables(kc, k, pk)) == 0) {
             if (!searched) {
                 TreeNode<K,V> q, ch;
                 searched = true;
                 if (((ch = p.left) != null &&
                      (q = ch.find(h, k, kc)) != null) ||
                     ((ch = p.right) != null &&
                      (q = ch.find(h, k, kc)) != null))
                     return q;
             }
             dir = tieBreakOrder(k, pk);
         }

         TreeNode<K,V> xp = p;
         if ((p = (dir <= 0) ? p.left : p.right) == null) {
             Node<K,V> xpn = xp.next;
             TreeNode<K,V> x = map.newTreeNode(h, k, v, xpn);
             if (dir <= 0)
                 xp.left = x;
             else
                 xp.right = x;
             xp.next = x;
             x.parent = x.prev = xp;
             if (xpn != null)
                 ((TreeNode<K,V>)xpn).prev = x;
             moveRootToFront(tab, balanceInsertion(root, x));
             return null;
         }
     }
 }

2.总结

实现过程分为以下几步

  1. 判断数组是否为空或数组长度是否为空,没有立马扩容
  2. 确定插入元素在数组的位置,算法是hash&(cap -1)
  3. 如果该位置没有存放元素,直接将该元素存在在此
  4. 如果该位置是红黑树,则会遍历红黑树去看是否有相同值,有则返回该节点,没有则会插入到红黑树中
  5. 如果该位置是链表,则会遍历链表查看是否有相同值,有则返回该节点,没有会将元素插入到链表末尾,并判断节点达到8了没,达到则进行树化
  6. 如果有相同值则进行替换,修改次数加1,并判断是否需要扩容,需要则扩容

3.其他关于hashmap的分析

hashmap的常见静态属性和方法
hashmap的扩容机制
hashmap链表转化成红黑树的过程以及红黑树转化成链表的过程

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

hashmap的存放元素的实现过程 的相关文章

  • 如何查看Pocketsphinx词典中是否存在该单词?

    我只是想看看字典文件中是否存在字符串 字典文件位于问题底部 我想检查语音识别器是否可以识别单词 例如 识别器将无法识别字符串ahdfojakdlfafiop 因为字典中没有定义 所以 我可以检查某个单词是否在 pocktsphinx 词典中
  • 任务“:app:dexDebug”执行失败

    我目前正在处理我的项目 我决定将我的 Android Studio 更新到新版本 但在我导入项目后 它显示如下错误 Information Gradle tasks app assembleDebug app preBuild UP TO
  • java.lang.Class: 在 java 程序中初始化 log4j 属性文件时出错

    我正在尝试使用 log4j 运行独立的 java 程序 但在调试时收到以下消息 控制台上没有 log4j 相关日志 log Logger 1343 java lang Class ERROR in 18b4aac2 有人可以建议这里出了什么
  • 如何对 IntStream 进行逆序排序

    我正在使用 txt 文件读取数字BufferedReader 我想颠倒该流中元素的顺序 以便在收集它们时 它们将从最高到最低排列 我不想在构建数组后进行排序 因为我不知道其中可能有多少元素 我只需要最高的 N 个元素 in new Buff
  • Java 重写 hashCode() 得到 StackOverflowError

    所以我不太熟悉重写 hashCode 并且我似乎在 hashCode 方法中以某种方式进行了一些无限递归 这是我的场景 我有一个 DuplicateCache 类 它是一个缓存对象 用于检查系统中的重复对象 我有一个静态内部类 Duplic
  • PropertySources 中各种源的优先级

    Spring引入了新的注释 PropertySources对于所有标记为的类 Configuration since 4 0 需要不同的 PropertySource作为论证 PropertySources PropertySource c
  • Java 变量的作用域

    我不明白为什么这段代码的输出是10 package uno public class A int x 10 A int x 12 new B public static void main String args int x 11 new
  • spring - 强制 @Autowired 字段的 cglib 代理

    我有混合堆栈 EJB 和 Spring 为了将 Spring 自动装配到 EJB 我使用SpringBeanAutowiringInterceptor 不确定这是否会影响我遇到的问题 在尝试通过以下方式自动装配 bean 时 Scope p
  • 如何将 XMP XML 块序列化为现有的 JPEG 图像?

    我有许多 JPEG 图像 其中包含损坏的 XMP XML 块 我可以轻松修复这些块 但我不确定如何将 固定 数据写回图像文件 我目前正在使用 JAVA 但我愿意接受任何能让这项任务变得容易的事情 这是目标关于 XMP XML 的另一个问题
  • 想要开发像 Facebook 这样的网站 - 处理数百万个请求 - 高性能 [关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我想用 Java 开发一个像 Fac
  • 如何在java中使jpeg无损?

    有没有人可以告诉我如何使用编写 jpeg 文件losslessjava中的压缩 我使用下面的代码读取字节来编辑字节 WritableRaster raster image getRaster DataBufferByte buffer Da
  • 打印包含 JBIG2 图像的 PDF

    请推荐一些库 帮助我打印包含 JBIG2 编码图像的 PDF 文件 PDFRenderer PDFBox别帮我 这些库可以打印简单的 PDF 但不能打印包含 JBIG2 图像的 PDF PDFRenderer尝试修复它 根据 PDFRedn
  • 覆盖 MATLAB 默认静态 javaclasspath 的最佳方法

    MATLAB 配置为在搜索用户可修改的动态路径之前搜索其静态 java 类路径 不幸的是 静态路径包含相当多非常旧的公共库 因此如果您尝试使用新版本 您可能最终会加载错误的实现并出现错误 例如 静态路径包含 google collectio
  • jmap - 组织和堆操作会给 jvm 带来开销吗?

    正如标题所述 需要多少开销jmap histo and jmap heap分别带到jvm 如果一个内存敏感的 Java 进程处于OutOfMemory 例如 大约 96 的堆已满 并且无法通过 full gc 清除 其中一项操作是否有可能将
  • 如何为 Jackson 编写一个包罗万象的(反)序列化器

    当您提前知道类型时 编写自定义序列化器非常容易 例如 MyType一个人可以写一个MyTypeSerializer extends StdSerializer
  • 使用 Java 从 S3 上的文件在 S3 上创建 zip 文件

    我在 S3 上有很多文件 需要对其进行压缩 然后通过 S3 提供压缩文件 目前 我将它们从流压缩到本地文件 然后再次上传该文件 这会占用大量磁盘空间 因为每个文件大约有 3 10MB 而且我必须压缩多达 100 000 个文件 所以一个 z
  • 如何在android sdk上使用PowerMock

    我想为我的 android 项目编写一些单元测试和仪器测试 然而 我遇到了一个困扰我一段时间的问题 我需要模拟静态方法并伪造返回值来测试项目 经过一些论坛的调查 唯一的方法是使用PowerMock来模拟静态方法 这是我的 gradle 的一
  • 从一个文本文件中获取数据并将其移动到新的文本文件

    我有一个文件 里面有数据 在我的主要方法中 我读入文件并关闭文件 我调用另一种方法 在原始文件的同一文件夹内创建一个新文件 所以现在我有两个文件 原始文件和通过我调用的方法生成的文件 我需要另一种方法 从原始文件中获取数据并将其写入创建的新
  • Java 编码风格、局部变量与重复方法调用

    我更喜欢使用局部变量而不是多次调用同一方法 I prefer this Vehicle vehicle person getVehicle if vehicle instanceof Car Car car Car vehicle car
  • 尝试使用带有有效购买令牌的 Java Google Play Developer API v3 检索应用内购买信息时出现错误请求(无效值)

    当使用 Java Google Play Developer API 版本 3 并请求有效购买令牌的购买信息时 我收到以下异常 API 调用返回 400 Bad Request 响应以及以下消息 code 400 errors domain

随机推荐

  • Static

    Static 回忆c语言中static的作用 修饰局部变量时延长了局部变量的生命周期 修饰全局变量时限制了全局变量的作用域 修饰函数时限制了函数的作用域 Static修饰成员变量 必须在类内声明在类外定义 原因 static修饰的变量在编译
  • Input.GetAxis();

    Input GetAxis 就是获取鼠标移动相对于上个位置的相对度量值 括号里面填的是相应坐标轴名称 例如 float x Input GetAxis Horizontal Time deltaTime speed float z Inpu
  • 天池大赛中药说明书实体识别挑战冠军方案开源(一)方案及模型原理说明

    目录 Introduction 导言 赛题背景 任务描述 数据探索分析 核心思路 数据预处理 Baseline BERT CRF 优化1 对抗训练 优化2 混合精度训练 FP16 优化3 多模型融合 优化4 半监督学习 其他无明显提升的尝试
  • c#图像常规处理

    using OpenCvSharp using System using System Collections Generic using System Drawing using System Linq using System Xml
  • vue动态渲染echarts,以及多次调用组件数据更新时组件无法同步刷新详解

    数据在vue中是被灵活操作的 当遇到如图这种echarts的数据需要通过接口获取 并且进行相应的删除和添加操作时 echarts的数量与数据要与数据同步刷新 想要独立的echarts动态渲染 我想到通过封装echarts组件通过props传
  • facebooksdk demo的使用

    更多消息查看 https developers facebook com docs android getting started 下载demo地址 https developers facebook com resources faceb
  • TCP头部详解

    1 TCP的定义 TCP提供一种面向连接的 可靠的字节流服务 面向连接 两个使用TCP的应用 通常是一个客户和一个服务 在彼此交换数据之前必须建立一个TCP连接 TCP提供可靠性的方式 1 应用数据被分割成TCP认为最适合发送的数据块 2
  • 关于图片操作记录

    1 在img标签的src属性中可以是图片路径 也可以是base64位编码的图片格式 base64图片格式 var images format jpg data data image jpeg base64 9j 4AAQSkZJRgABAQ
  • 【基于Android的ARM汇编语言系列】之二:C/C++程序生成ARM汇编程序的过程分析

    作者 郭嘉 邮箱 allenwells 163 com 博客 http blog csdn net allenwells github https github com AllenWell 基于Android的ARM汇编语言系列 章节列表
  • 解决kubernetes默认证书1年有效期问题

    https blog 51cto com 11889458 2323328
  • HDMI学习笔记

    文章目录 一 HDMI 基本介绍 二 TMDS基本介绍 三 传输流程 四 传输周期 五 Data Island Packet结构 六 Audio Clock 七 HotPlug 八 HDMI Sink 九 HDMI版权内容保护之HDCP 十
  • NodeJS - 第一个应用程序Hello World

    安装NodeJs 在创建实际的 Hello World 应用之前 我们应该先安装NodeJS 安装NodeJS可以访问NodeJS官网 下载相应系统的NodeJS的安装包 进行安装 程序组件 关于Hello World 这个应用主要包括三部
  • Leetcode4.寻找两个有序数组的中位数——巧妙使用二分法

    文章目录 引入 Leetcode题解 引入 本题是这样的 4 寻找两个有序数组的中位数 给定两个大小为 m 和 n 的有序数组 nums1 和 nums2 请你找出这两个有序数组的中位数 并且要求算法的时间复杂度为 O log m n 你可
  • TypeScript语法1

    TypeScript 1 安装 npm install g typescript gt tsc v 版本 gt vscode新建 ts 文件 gt tsc hello ts 转化为js文件 gt node hello js 终端运行 2 T
  • zookeeper和kafka的安全认证机制SASL(动态)

    1 创建管理员用户 用户 密码 admin admin 老版本创建方法 启动zookeeper 未启动broker sh bin kafka configs sh zookeeper localhost 2181 alter add con
  • 批量写xml和添加数据到数据库

    目录 1 批量添加数据到数据库 1 1 Python与sqlite数据库连接 1 2 单条添加数据到数据库 1 3 批量添加数据到数据库 2 批量创建xml 2 1 创建xml 2 2 批量xml 1 批量添加数据到数据库 1 1 Pyth
  • 解决EasyExcel工具读取Excel空数据行的问题

    EasyExcel是Alibaba开源的一个Java处理Excel的工具 官网解读 快速 简洁 解决大文件内存溢出的java处理Excel工具 快速 快速的读取excel中的数据 简洁 映射excel和实体类 让代码变的更加简洁 大文件 在
  • pandas通过索引进行排序

    import pandas as pd df pd DataFrame 1 2 3 4 5 index 10 52 24 158 112 columns S df sort index inplace True print df
  • PyTorch SGD 中参数 Momentum 的理解

    动量 他的作用是尽量保持当前梯度的变化方向 没有动量的网络可以视为一个质量很轻的棉花团 风往哪里吹就往哪里走 一点风吹草动都影响他 四处跳动不容易学习到更好的局部最优 没有动力来源的时候可能又不动了 加了动量就像是棉花变成了铁球 咕噜咕噜的
  • hashmap的存放元素的实现过程

    1 代码实现 存放运算的方法 hash key 即获取key的hash码值 算法为 key hashcode key hashcode gt gt gt 16 前面有分析 public V put K key V value return