首页 > 其他分享 >HashMap 的扩容机制

HashMap 的扩容机制

时间:2024-12-01 10:57:51浏览次数:7  
标签:扩容 Node node HashMap int key 机制

目录

一、HashMap 基本架构概览

二、扩容机制全解析

负载因子

扩容阈值

扩容操作步骤详解

三、代码实例呈现

四、总结与启示


在 Java 的世界里,HashMap 占据着极为重要的一席之地。它依托哈希表来构建,这种设计使得其在插入、删除以及查找操作上能够展现出相当快速的效率。不过,就像任何事物在面临规模增长时都会遭遇挑战一样,随着 HashMap 中元素数量的持续攀升,它的性能表现可能会大打折扣。为了能够始终维持高效的运行状态,HashMap 巧妙地引入了扩容机制。在接下来的内容中,我们将深入到 HashMap 的扩容机制内部,一探究竟,并且还会附上相应的代码示例来辅助理解。

一、HashMap 基本架构概览

在我们一头扎进扩容机制的复杂细节之前,先来简单回顾一下 HashMap 的基础结构。HashMap 主要是借助数组和链表(需要注意的是,在 Java 8 及其后续版本中,一旦链表的长度超出一定限度,链表就会被自动转换为红黑树)来实现数据存储的。HashMap 中的元素会被存放在数组的特定索引位置,而这个位置是通过哈希函数对键(Key)进行计算得出哈希值后确定的。

二、扩容机制全解析

当 HashMap 内部的元素数量累积到某个特定的界限时,扩容操作就会被触发,以此来确保操作效率不会因为元素过多而受到严重影响。这个特定的界限是由当前容量(capacity)与负载因子(load factor)这两个因素共同作用决定的。

负载因子

负载因子,从本质上来说,是一个用于衡量 HashMap 被填满程度的关键参数。它的计算方式是用 HashMap 中所包含的元素数量除以桶(bucket)的数量。在 Java 中,HashMap 默认设定的负载因子为 0.75f。

扩容阈值

扩容阈值的计算公式是:capacity * loadFactor。也就是说,当 HashMap 中的元素数量超过了由这个公式计算得出的阈值时,扩容操作就会紧锣密鼓地展开。

扩容操作步骤详解

  1. 首先,会创建一个全新的 Node 数组,这个新数组的容量将会是原数组容量的两倍。这就好比是为数据的存储开辟了一片更为广阔的空间,以容纳更多的元素。
  2. 接下来,需要对原 HashMap 中的每一个元素进行遍历操作。在遍历的过程中,针对每个元素,都要依据其键重新计算在新数组中的存储位置。这一步骤确保了元素在新的数组环境下能够被正确地安置。
  3. 最后,把原 HashMap 中的所有元素依次重新插入到新创建的数组之中,从而完成整个扩容过程,使得 HashMap 在新的容量基础上能够继续高效地运作。

三、代码实例呈现

以下是一段简化后的代码,用于展示 HashMap 的扩容机制:

import java.util.HashMap;

public class HashMapExample {

    // 内部节点类,用于表示 HashMap 中的元素
    private static class Node {
        int key;
        int value;
        Node next;

        Node(int key, int value, Node next) {
            this.key = key;
            this.value = value;
            this.next = next;
        }
    }

    // 存储元素的数组
    private Node[] buckets;
    // 当前 HashMap 中元素的数量
    private int size = 0;
    // 负载因子,设定为默认值 0.75f
    private final float loadFactor = 0.75f;
    // 扩容阈值
    private int threshold; 

    // 构造函数,初始化 HashMap 的容量并计算扩容阈值
    public HashMapExample(int initialCapacity) {
        buckets = new Node[initialCapacity];
        threshold = (int) (initialCapacity * loadFactor);
    }

    // 向 HashMap 中插入元素的方法
    public void put(int key, int value) {
        // 判断当前元素数量是否即将达到扩容阈值,如果是,则进行扩容操作
        if (size + 1 >= threshold) {
            resize();
        }
        // 计算元素在数组中的索引位置
        int index = hash(key);
        Node node = buckets[index];
        // 遍历链表,查看是否存在相同的键,如果有则更新对应的值
        for (; node!= null; node = node.next) {
            if (node.key == key) {
                node.value = value;
                return;
            }
        }
        // 如果不存在相同的键,则将新元素插入到链表头部
        buckets[index] = new Node(key, value, buckets[index]);
        size++;
    }

    // 哈希函数,用于计算键的哈希值并确定在数组中的位置
    private int hash(int key) {
        return (key ^ (key >>> 16)) & (buckets.length - 1);
    }

    // 扩容方法
    private void resize() {
        // 创建新的数组,容量为原数组的两倍
        Node[] newBuckets = new Node[buckets.length * 2];
        // 遍历原数组中的每个元素
        for (Node node : buckets) {
            while (node!= null) {
                // 重新计算元素在新数组中的位置
                int index = hash(node.key);
                Node next = node.next;
                // 将元素插入到新数组的对应位置
                newBuckets[index] = new Node(node.key, node.value, newBuckets[index]);
                node = next;
            }
        }
        // 更新数组引用和扩容阈值
        buckets = newBuckets;
        threshold = (int) (buckets.length * loadFactor);
    }
}

四、总结与启示

HashMap 的扩容机制无疑是保障其高性能表现的核心要素之一。通过在恰当的时机进行扩容操作,HashMap 能够有效地控制哈希冲突发生的概率,进而始终保持高效的操作效率。对于我们这些在日常开发工作中频繁使用 HashMap 的开发者来说,深入透彻地理解 HashMap 的扩容机制具有极为重要的意义,它能够帮助我们更加合理、高效地运用 HashMap 来解决各种实际的编程问题,避免因对其内部机制的不了解而导致的性能瓶颈或错误使用。

标签:扩容,Node,node,HashMap,int,key,机制
From: https://blog.csdn.net/weixin_73687229/article/details/144144028

相关文章

  • 多头注意力机制:从原理到应用的全面解析
    目录什么是多头注意力机制?原理解析1.注意力机制的核心公式2.多头注意力的扩展为什么使用多头注意力?实际应用1.Transformer中的应用2.NLP任务3.计算机视觉任务PyTorch实现示例总结        近年来,“多头注意力机制(Multi-HeadAttention,MHA)”成为深......
  • 流水线并行,重计算:GPipe;1F1B(一前一后)调度机制
    目录GPipe一、GPipe的背景与目的二、GPipe的功能与特点三、GPipe的应用与效果四、GPipe的开源与可扩展性1F1B(一前一后)调度机制一、背景与基本概念二、1F1B调度机制的要求三、应用与挑战GPipe是一个基于Lingvo(Lingvo是Google基于TensorFlow二次开发的,重点针对序列......
  • MySQL原理简介—5.存储模型和数据读写机制
    MySQL原理简介—5.存储模型和数据读写机制大纲1.为什么不能直接更新磁盘上的数据2.为什么要引入数据页的概念3.一行数据在磁盘上是如何存储的4.一行数据中的NULL值是如何处理的5.一行数据的数据头存储的是什么6.一行数据的真实数据如何存储7.数据在物理存储时的行溢出......
  • 数组扩容
    一、思路:新建一个数组,将以前的数组值依次放入新数组中,在新数组中添加元素;让以前数组等于新数组,以前数组的数据空间销毁;数组反转同理二、要求:实现动态的给数组添加元素效果,实现对数组扩容。ArrayAdd.java1.原始数组使用静态分配int[]arr={1,2,3}2.增加的元素4,直接放在数组的......
  • 深入了解 Eureka:微服务中的服务发现机制
    什么是Eureka?Eureka是Netflix开源的一个服务注册与发现工具,专为云原生架构设计。Eureka分为两个主要部分:EurekaServer:服务注册中心,负责管理所有服务实例的注册信息。EurekaClient:服务消费者和服务提供者都通过它与EurekaServer通信,用于注册自身服务或获取其他......
  • 泛型数组与hashmap
    5.3泛型数组列表5.3.1访问数组元素列表基本类型和包装类是等价的可以用int接受Integer的importjava.util.*;publicclasstext1{publicstaticvoidmain(String[]args)throwsException{String[]arr=newString[100];//长度定死ArrayList<S......
  • 直播短视频系统源码,一步步实现缓存机制
    直播短视频系统源码,一步步实现缓存机制1、逻辑冲突设计的伊始谈到,为了保证解耦,我们希望缓存机制不能修改播放器源码,但MediaPlayer如何在不改源码的情况下,将自身的缓存加载逻辑交给我们的CacheService呢?如下述代码中所展示的,这种实现似乎无法避免:publicclassMy......
  • 【Linux探索学习】第十七弹——进程终止:深入解析操作系统中的进程终止机制
    Linux学习笔记:https://blog.csdn.net/2301_80220607/category_12805278.html?spm=1001.2014.3001.5482前言:在操作系统中,进程终止是一个至关重要的阶段,它标志着进程的生命周期结束。进程终止可能是因为任务完成,也可能是因为异常或外部干预。本文将详细讲解操作系统中的进程......
  • Java基础——泛型(3)#HashMap泛型
    一、HashMap        HashMap最早出现在JDK1.2中,底层基于散列算法实现,它是一个key-value结构的容器。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。二、HashMap特点      ......
  • 为 Paddle2ONNX 搭建 Github Actions 自动发包机制
    1简介Paddle2ONNX此前一直使用手动编译所有版本的Python源码包再手动上传到PyPI的方式来分发发行版。很显然,这是一种极其低效的办法,本文介绍如何为Paddle2ONNX添加自动发包机制。2实现过程Paddle2ONNX的编译流程参考onnx的编译流程实现,因此在自动发包机制的设计上......