HashMap底层实现条分缕析

目录

题外话

正题

哈希表

哈希碰撞

HashMap底层实现

小结


题外话

又水了两天,怪我,在宿舍确实没什么状态,是时候调整调整了

正题

今天直接讲解HashMap底层实现

哈希表

哈希表又称散列表

是数组和单向链表的结合体

如下图

而哈希表存放元素机制是靠哈希函数解析关键字,使其转变为哈希值

然后再由这个哈希值和整个数组长度求模运算,放入求模算出对应的数组下标中

哈希碰撞

输入两个不同的值,经过哈希函数解析之后,哈希值模完数组长度是一样的

如下图:

当出现哈希碰撞时,我们就需要利用单向链表,将其放入同一下标的链表当中,这样也就解决了哈希碰撞

而且单向链表的插入和修改操作时间复杂度很小,只需要修改结点next域即可

大家可以想象一下,如果对数组进行增删时,我们普遍需要让数组一些元素往后移或者往前移,时间复杂度更高

HashMap底层实现

下面我们分两组代码让大家明白HashMap底层如何实现,我们这里先将key和value默认为int类型

public class HashMapBottom{

    class Node
    {
    /*
    创建key,value和next
     */

        int key;
        int  value;
        Node next;
        //提供构造方法
        public Node(int key, int value) {
            this.key = key;
            this.value = value;
        }
        /**
         *构造一个结点对象
         * @param key 键
         * @param value 值
         * @param next 指向下一个结点
         */

    }
    //负载因子为0.75
    private final float loadFactor=0.75f;

    /*
       哈希表
        */
    private Node[] table;

    /*
    记录哈希表元素数量
     */
    private int size;

    /*
    初始化容量
     */
    public HashMapBottom()
    {
        this.table=new Node[16];
    }

 
    public int size()
    {
    return size;
    }
    public void put(int key,int value)
    {
        //获取key模数组长度
        int index=key%table.length;
        //将index放入数组中,cur获取当前数组元素
        Node cur=table[index];
        //如果cur获得元素不为空
        while (cur!=null)
        {
            //判断cur取出元素是否和key相等,相等则说明重复了
            if (cur.key==key)
            {
                //如果相等则更新cur的value值
            cur.value=value;
            return;
            }
            //如果cur.key等于key,说明没有重复,插入到后面位置
            cur=cur.next;
        }
        //如果cur为空,则创建结点存放当前元素
        Node node=new Node(key,value);
        node.next=table[index];
        table[index]=node;
        size++;
        if (doloadFactor()>loadFactor)
        {
        resize();
        }
    }
    private void resize()
    {
        //将table数组进行二倍扩容
        Node[] newTable=new Node[2*table.length];
        for (int i=0;i<table.length;i++)
        {
            //将table数组中元素拿出
            Node cur=table[i];
            //如果取出元素不为空
            while (cur!=null)
            {
                //先将cur下一个结点放入tmp中
                Node tmp=cur.next;
                //将cur.key模newTable.length算出新的值
                int newIndex=cur.key%newTable.length;
                //将cur放入扩容数组中
                newTable[newIndex]=cur;
                //将之前cur保存的下一个结点传入cur继续循环
                cur=tmp;
            }
        }
        //全部放入扩容数组中之后,将扩容数组给到之前的数组
        table=newTable;
    }
    private float doloadFactor()
    {
        return size*1.0f/table.length;
    }
    public int get(int key)
    {
        //先找到key在数组的下标
        int index=key%table.length;
        //将数组下标元素赋值cur
        Node cur=table[index];
        //当cur不为空
        while (cur!=null)
        {
            //如果cur.key==key说明找到了,返回cur的value即可
            if (cur.key==key)
            {
                return cur.value;
            }
            //如果不等于则进入下一个结点继续找
            cur=cur.next;
        }
        //如果遍历完成全部结点则返回-1
        return -1;
    }

}

我们如果使用的是基本数据类型包装类,或者String类的话

里面是重写过HashCode()和equals()方法的,所以我们使用的时候不需要过于担心

但是当我们自己创建类例如Student或者User之类的类

我们一定要重写HashCode()和equals()方法

比如我们在HashMap创建对象中调用put方法,添加了Student类,我们如果不重写上面两个方法

我们就没法比较,也没法哈希碰撞,如果put两个key值一样的元素,没有重写方法,就会将这两个元素全部放入HashMap中

下面让我们稍微实现一下带泛型的底层代码

public class HashMapBottom02<K,V> {

//创建key和value还有next
    class Node<K,V>{
        K key;
        V value;
        Node<K,V> next;
        public Node(K key,V value)
        {
            this.key=key;
            this.value=value;
        }
    }
    Node<K,V>[] table;
    private int size;
    private static final float loadFactor=0.75f;
    public HashMapBottom02()
    {
        table=new Node[16];
    }
    public void put(K key,V value) {
        int hash=key.hashCode();

        //获取key模数组长度
        int index = hash % table.length;
        //将index放入数组中,cur获取当前数组元素
        Node<K,V> cur = table[index];
        //如果cur获得元素不为空
        while (cur != null) {
            //判断cur取出元素是否和key相等,相等则说明重复了,这里比较一定要用equals,不然比较的是地址,而不是数值大小
            if (cur.key.equals(key) ) {
                //如果相等则更新cur的value值
                cur.value = value;
                return;
            }
            //如果cur.key等于key,说明没有重复,插入到后面位置
            cur = cur.next;
        }
        //如果cur为空,则创建结点存放当前元素
        Node<K,V> node = new Node(key, value);
        node.next = table[index];
        table[index] = node;
        size++;
    }
    public V get(K key)
    {
        int hash=key.hashCode();
        int index=hash%table.length;
       Node<K,V> cur=table[index];
        //当cur不为空
        while (cur!=null)
        {
            //如果cur.key==key说明找到了,返回cur的value即可
            if (cur.key.equals(key))
            {
                return cur.value;
            }
            //如果不等于则进入下一个结点继续找
            cur=cur.next;
        }
        //如果遍历完成全部结点则返回-1
        return null;
    }
}

小结

明天直接更新算法题,最近堕落了,赶紧调整下状态

麻烦喜欢的家人们三连一下(点赞关注收藏!!!)

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

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

相关文章

Jira搭建过程

看到很多小伙伴对jira有兴趣,我们今天就来分享一下jira的搭建吧 首先要明白jira是什么? 看来搭建jira也是我们测试人员需要具备的技能之一了.下面是详细的大家步骤: 1.系统环境准备 Centos 7.5 Mysql 5.6 Java1.8 2.软件安装包 atlassian-jira-software-7.13.0-x64.bin …

Linux_环境变量

目录 1、查询所有环境变量 2、常见的环境变量 2.1 PATH 2.2 HOME 2.3 PWD 3、增加新的环境变量 4、删除环境变量 5、main函数的三个形参 5.1 argv字符串数组 5.2 env字符串数组 6、系统调用接口 6.1 getenv 6.2 putenv 7、全局变量environ 结语 前言&…

SpringBoot + kotlin 协程小记

前言&#xff1a; Kotlin 协程是基于 Coroutine 实现的&#xff0c;其设计目的是简化异步编程。协程提供了一种方式&#xff0c;可以在一个线程上写起来像是在多个线程中执行。 协程的基本概念&#xff1a; 协程是轻量级的&#xff0c;不会创建新的线程。 协程会挂起当前的协…

中颖51芯片学习9. PWM(12bit脉冲宽度调制)

中颖51芯片学习9. PWM&#xff08;12bit脉冲宽度调制&#xff09; 一、资源简介二、PWM工作流程三、寄存器介绍1. PWMx控制寄存器PWMxCON2. PWM0周期寄存器PWM0PH/L3. PWM1周期寄存器PWM1PH/L4. PWM0占空比控制寄存器PWM0DH/L5. PWM1占空比控制寄存器 PWM1DH/L6. 占空比寄存器与…

跨语言指令调优深度探索

目录 I. 介绍II. 方法与数据III. 结果与讨论1. 跨语言迁移能力2. 问题的识别3. 提高跨语言表现的可能方向 IV. 结论V. 参考文献 I. 介绍 在大型语言模型的领域&#xff0c;英文数据由于其广泛的可用性和普遍性&#xff0c;经常被用作训练模型的主要语料。尽管这些模型可能在英…

ESLlint重大更新后,使用旧版ESLint搭配Prettier的配置方式

概要 就在前几天&#xff0c;ESLint迎来了一次重大更新&#xff0c;9.0.0版本&#xff0c;根据官方文档介绍&#xff0c;使用新版的先决条件是Node.js版本必须是18.18.0、20.9.0&#xff0c;或者是>21.1.0的版本&#xff0c;新版ESLint将不再直接支持以下旧版配置(非扁平化…

二、OSPF协议基础

基于SPF算法&#xff08;Dijkstra算法&#xff09;的链路状态路由协议OSPF&#xff08;Open Shortest Path First&#xff0c;开放式最短路径优先&#xff09; 目录 1.RIP在大型网络中部署所面临的问题 2.Router ID 3.OSPF的报文 4.OSPF邻居建立过程 5.OSPF报文的确认机制…

SAP的生成式AI

这是一篇openSAP中关于SAP生成式AI课程的笔记,原地址https://open.sap.com/courses/genai1/ 文章目录 Unit 1: Approaches to artificial intelligence概念三种范式监督学习非监督学习强化学习Unit 2: Introduction to generative AI生成式AI基础模型关系基础模型有哪些能力呢…

怎么通过isinstance(Obj,Class)验证?【isinstance】

最近有这样一个项目&#xff0c;这个项目可以用一个成熟的项目的构造树&#xff0c;读取树&#xff0c;再检索的过程&#xff0c;现在有新的需求&#xff0c;另一个逻辑构造同样节点结构的树&#xff0c;pickle序列化保存&#xff0c;再使用原来项目的读取、检索函数&#xff0…

一年期免费SSL证书申请方法

免费SSL证书的申请已经成为当今互联网安全实践中的重要环节&#xff0c;它不仅有助于保护网站数据传输的隐私性和完整性&#xff0c;还能提升用户信任度&#xff0c;因为现代浏览器会明确标识出未使用HTTPS&#xff08;即未部署SSL证书&#xff09;的网站为“不安全”。以下是一…

互联网安全面临的全新挑战

前言 当前移动互联网安全形势严峻&#xff0c;移动智能终端漏洞居高不下、修复缓慢&#xff0c;移动互联网恶意程序持续增长&#xff0c;同时影响个人和企业安全。与此同时&#xff0c;根据政策形势移动互联网安全监管重心从事前向事中事后转移&#xff0c;需加强网络安全态势感…

玩转必应bing国内广告投放,正确的攻略方式!

搜索引擎广告作为精准触达潜在客户的重要渠道&#xff0c;一直是众多企业营销策略中的关键一环&#xff0c;在国内市场&#xff0c;虽然百度占据主导地位&#xff0c;但必应Bing凭借其独特的用户群体、高质量的搜索体验以及与微软生态的紧密集成&#xff0c;为广告主提供了不可…

相关运算及实现

本文介绍相关运算及实现。 相关运算在相关检测及数字锁相放大中经常用到&#xff0c;其与卷积运算又有一定的联系&#xff0c;本文简要介绍其基本运算及与卷积运算的联系&#xff0c;并给出实现。 1.定义 这里以长度为N的离散时间序列x(n),y(n)为例&#xff0c;相关运算定义如…

nvm管理多个node版本,快速来回切换node版本

前言 文章基于 windows环境 使用nvm安装多版本nodejs。 最近公司有的项目比较老需要降低node版本才能运行&#xff0c;由于来回进行卸载不同版本的node比较麻烦&#xff1b;所以需要使用node工程多版本管理&#xff0c;后面自己就简单捯饬了一下nvm来管理node&#xff0c;顺便…

VTK----VTK数据结构详解2(计算机篇)

在VTK中&#xff0c;属性数据和点都用数据数组&#xff08;data arrays&#xff09;表示。某些属性数据&#xff08;例如法线、张量&#xff09;需要具有与其定义一致的元组&#xff08;在计算机编程中&#xff0c;元组&#xff08;tuple&#xff09;用来表示存储多种数据类型的…

vue下载文件时显示进度条

1.单个下载&#xff08;开始是导出按钮 下载显示进度条&#xff09; html <el-button click.stop"exportReport(scope.row, scope.index)" v-if"!scope.row.schedule" icon"el-icon-download"size"small" type"text"styl…

cocos-lua资源管理

本文介绍cocos-lua项目的资源管理和工作流&#xff0c;适用人群包括初学者和有经验开发者&#xff0c;故读者可根据自己的需要有选择性的查阅自己需要的内容 一.简单案例解析 下文通过介绍一个简单demo&#xff0c;介绍合图和资源目录结构 1.1 运行效果 1.2 ccs结构 1.3 目录…

【Python-Spark(大规模数据)】

Python-Spark&#xff08;大规模数据&#xff09; ■ Spark■ PySparl编程模型■ 基础准备■ 数据输入■ RDD的map成员方法的使用■ RDD的flatMap成员方法的使用■ RDD的reduceByKey成员方法的使用■ 单词计数统计■ RDD的filter成员方法的使用■ RDD的distinct成员方法的使用■…

LANGUAGE-DRIVEN SEMANTIC SEGMENTATION

环境不易满足&#xff0c;不建议复现

详解js中的console对象

对于前端开发而言&#xff0c;console对象大家肯定都很熟悉&#xff0c;最常用的 console.log() 是开发调试必用的 但是对于console对象的其他方法&#xff0c;相对而言使用的就比较少了。下面详细介绍一下&#xff1a; 谷歌浏览器输出console对象&#xff1a; 值得一提的是不…