【数据结构】HashMap的实现与常见问题

news2024/11/14 18:19:44

目录

一、概念

哈希算法与哈希表

二、哈希碰撞与解决

解决哈希冲突的四种方法

HashMap解决哈希冲突

三、代码实现

四、HashMap面试题

两个对象的hashcode一样那么equals一定一样吗?&& 两个对象的equals一样那么hashcode一定一样吗?

HashMap m = new HashMap<>底层数组多大?

HashMap m = new HashMap<25>底层数组多大?

扩容需要注意什么

讲一下你了解的HashMap


一、概念

哈希算法与哈希表
  • 哈希算法就是把任意长度的输入通过散列算法变为固定长度的输出,这个输出的结果就是散列值。
  • 哈希表也叫做散列表,他是通过key直接访问在内存存储位置的数据结构,在具体实现上,我们通过哈希函数把key映射到表中的某个位置,来获取这个位置的数据从而加快查询速度
  • 哈希冲突是由于哈希算法被计算的数据是无限的,而计算后的结果范围有限,所以会出现不同数据经过计算后得到的散列值相同

二、哈希碰撞与解决

解决哈希冲突的四种方法
  • 开发地址法:也叫线性探测法,就是从发生哈希冲突的位置开始按照一定的顺序依次此功能哈希表中找到一个空闲位置将该元素存储。
  • 链式寻址法:将存在哈希冲突的key,以单向链表的方式来存储。
  • 再哈希法:当通过某个哈希函数计算出现冲突后,再使用另一个哈希函数计算散列值,直到不出现冲突为止。这种方法会增加计算时间,性能影响比较大
  • 建立公共溢出区:将哈希表分为基本表与溢出表两个部分,出现哈希冲突的元素一律存放到溢出表中
HashMap解决哈希冲突

在jdk1.8版本中他是使用链式寻址法+红黑树解决哈希冲突问题的,其中红黑树是为了优化由于链表过长导致查询时间复杂度增加的问题,当数组长度超过64且单链表元素大于等于8时就会转变为红黑树

三、代码实现

public class HashMap <K, V>{
    // 节点
    static class Node<K, V> {
        K key;
        V val;
        public Node<K, V> next;

        public Node(K key, V val) {
            this.key = key;
            this.val = val;
        }
    }

    // 底层数组
    private Node<K, V>[] elem = new Node[16];
    // 元素个数
    private int size;

    // 负载因子
    private static final float DEFAULT_LOAD_FACTOR = 0.75f;

    public boolean put(K key, V data) {
        // 计算所在哈希桶
        int index = key.hashCode() % this.elem.length;
        // 遍历该哈希桶内链表
        Node<K, V> cur = this.elem[index];
        while (cur != null) {
            // 如果已经存在key,就替换
            if (cur.key.equals(key)) {
                cur.val = data;
                return false;
            }
            cur = cur.next;
        }
        // 如果没有就进行头插
        Node<K, V> node = new Node<>(key, data);
        node.next = this.elem[index];
        this.elem[index] = node;
        this.size++;

        // 计算是否需要扩容
        if (this.size >= this.elem.length * DEFAULT_LOAD_FACTOR) grow();

        return true;
    }

    public V get(K key) {
        // 计算下标
        int index = key.hashCode() % this.elem.length;
        // 开始遍历寻找
        Node<K, V> cur = this.elem[index];
        while (cur != null) {
            if (cur.key.equals(key)) {
                return cur.val;
            }
            cur = cur.next;
        }
        return null;
    }

    private void grow() {
        // 扩容
        Node<K, V>[] nodes = new Node[this.elem.length * 2];

        // 遍历原哈希表,由于计算元素所在哈希桶的规则随着数组长度变化而发生了改变所以将数据进行重新哈希
        for (int i = 0; i < this.size; i++) {
            Node<K, V> cur = this.elem[i];
            while (cur != null) {
                int index = cur.key.hashCode() % nodes.length;
                Node<K, V> next = cur.next;
                cur.next = nodes[index];
                nodes[index] = cur;
                cur = next;
            }
        }

        // 完成扩容
        this.elem = nodes;
    }

    public static void main(String[] args) {
        HashMap<Integer, Integer> map = new HashMap<>();
        map.put(1,1);
        map.put(222,1);
        map.put(333,1);
        map.put(1,111111111);
        map.put(22222,12);

        System.out.println(map.get(1));
    }
}

四、HashMap面试题

两个对象的hashcode一样那么equals一定一样吗?&& 两个对象的equals一样那么hashcode一定一样吗?

两个对象的hashCode相同,并不意味着它们的equals方法返回值一定相同。具体分析如下:

  1. hashCode的设计初衷:是为了提高哈希表等数据结构的性能,而equals方法则是用来比较两个对象是否在逻辑上相等。在Java中,如果两个对象通过equals()方法比较结果为true,那么这两个对象的hashCode()方法必须返回相同的值;反之,如果两个对象的hashCode()相同,并不意味着这两个对象就一定是相等的。
  2. 重写equals和hashCode的影响:当开发者在自定义类时重写了equals()方法和hashCode()方法,并且没有遵循正确的契约(即equals()为true时,hashCode()必须相同),就可能导致hashCode相同但equals不相同的情况出现。这通常是因为重写的方法中比较了不同的属性或使用了不同的逻辑。
  3. hashCode的冲突:即便是两个不相等的对象,它们的hashCode也可能会相同,这种情况称为哈希冲突。哈希冲突发生时,即使两个对象的hashCode一样,它们的equals方法也会返回false,因为它们实际上是不同的对象。

综上所述,虽然两个对象的hashCode相同不保证其equals结果一定相同,但是若两个对象equals方法返回true,则它们的hashCode必定相同。在Java编程实践中,正确重写equals和hashCode方法是非常重要的,以确保符合其契约并维持哈希表等相关数据结构的正确性。

HashMap m = new HashMap<>底层数组多大?

此时没有给数组开辟空间。当进行第一次put时会被初始化为16,详见下面Put流程

HashMap m = new HashMap<25>底层数组多大?

查看对应的构造方法

查看tableSizeFor方法

所以当我们构造方法传入25时,在开辟空间时会初始化为32

扩容需要注意什么

由于扩容使得哈希表的长度发生了改变,也意味着插入与获取元素的规则发生了改变,在寻找下标时我们使用key的哈希值与数组长度进行取模运算,扩容使得数组长度发生了变化,所以我们在进行扩容时需要对原有哈希表的元素进行重新哈希,让其满足新的规则

讲一下你了解的HashMap

常见字段

构造方法

哈希方法

为什么哈希要使用低16位与高16位进行异或操作呢?

为了降低冲突的概率,例如小李是某某省的人,他的身份证开头是433432.后面是200011120882,小明是另一个省的他的开头是122121,后面也是200011120882,如果取低位那么他与小李就会冲突,而低位所包含的信息与高位包含的信息进行异或后则会降低冲突的概率

Put方法

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

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

相关文章

灵敏度都那么低了,为什么CCA的阈值还是-82

实际这个值的确是可以变化的。只是一般都不怎么变。 主要决定于两个因素。一个因素是邻区的干扰。如果干扰弱&#xff0c;那么就可以调高一点。跟布网有关。那么其实在家庭环境&#xff0c;这个不好控制。所以CCA阈值不好调整。但是如果仔细测量邻区的干扰&#xff0c;依然是可…

宝塔linux部署react项目 图文并茂 傻瓜式教学

华为云服务器购买与宝塔linux系统安装 购买服务器过程省略&#xff0c;记得付款就好&#xff0c;或者新人可去白嫖一个月试用 linux服务器安装宝塔 宝塔官网 不同版本安装命令 系统安装脚本Centos安装脚本yum install -y wget && wget -O install.sh https://downlo…

Android Studio轮播图使用失败怎么办【已解决】

Android Studio轮播图使用失败怎么办 1.在gethub上面搜索轮播图 2.选择要使用的轮播图 3.查看该轮播图的配置方法 4.复制该依赖放入build.gradle中 5.重新构建 6.使用banner 发现没有报错了 7.参考网址 https://github.com/youth5201314/banner

ELK-介绍及Elasticsearch集群搭建

ELK是三个开源软件的缩写&#xff0c;分别为Elasticsearch、Logstash、kibana它们都是开源软件。后来新增了一个FileBeat&#xff0c;它是一个轻量及的日志收集处理工具&#xff0c;因为Logstash由java程序开发&#xff0c;比较消耗内存资源&#xff0c;后来将Logstash使用go语…

解决gpt无法发送对话的问题

问题描述 如图&#xff0c;今天登上去发现怎么无法发送消息 解决 可能是cookie问题&#xff0c;重新删除了就行了 cookie删除后&#xff0c;需要重新登录&#xff0c;主题色也重置为原来的白色了

Node.js与Webpack笔记(二)

上一篇&#xff1a;Node.js与Webpack笔记&#xff08;一&#xff09;-CSDN博客 Webpack模块打包工具 1.Webpack简介以及体验 webpack是一个静态模块打包工具&#xff0c;从入口构建依赖图&#xff0c;打包有关的模块&#xff0c;最后用于展示你的内容 静态模块&#xff1a;编写…

ky10 server 银河麒麟服务器主备搭建 (nginx+keepalived)

下载脚本代码 git clone https://gitcode.net/zengliguang/nginx_keepalived_ky10_x.git 进入脚本路径 更新脚本代码 更新完成 执行安装脚本 安装nginx离线编译安装依赖 解压nginx源码 检查环境 编译 nginx安装成功 安装keepalived keepalived安装成功

华为OD机试真题-测试用例执行计划

测试用例执行计划 题目描述&#xff1a; 某个产品当前迭代周期内有N个特性({F1,F2,...,FN})需要进行覆盖测试&#xff0c;每个特性都被评估了对应的优先级&#xff0c;特性使用其ID作为下标进行标识。 设计了M个测试用例({T1,T2,...,TM})&#xff0c;每个用例对应了一个覆盖特…

【Python刷题】环形链表

问题描述 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置&a…

SwiftUI之CoreData详解(一)

coreData 是一种数据持久化的方案&#xff0c;是对SQLite的一种封装。一说到这种桌面化的数据库&#xff0c;我就无比的怀念Foxbase|Foxpro, 多好的数据库产品&#xff0c;被微软扼杀了&#xff0c;相当年教大学生妹子们国家二级数据库时都是手把手教的&#xff0c;呃~~~&#…

解决火狐浏览器访问地址受限制问题(This address is restricted)

问题如下图&#xff1a; This address is restrictedThis address uses a network port which is normally used for purposes other than Web browsing. Firefox has canceled the request for your protection. 此地址受到限制 此地址使用通常用于 Web 浏览以外的目的的网…

21.网络游戏逆向分析与漏洞攻防-网络通信数据包分析工具-配置数据保存到文件

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果 内容参考于&#xff1a; 易道云信息技术研究院VIP课 上一个内容&#xff1a;20.数据分析工具…

人才推荐 | 毕业于美国凯斯西储大学的博士,专业知识广泛

编辑 / 木子 审核 / 朝阳 伟骅英才 伟骅英才致力于以大数据、区块链、AI人工智能等前沿技术打造开放的人力资本生态&#xff0c;用科技解决职业领域问题&#xff0c;提升行业数字化服务水平&#xff0c;提供创新型的产业与人才一体化服务的人力资源解决方案和示范平台&#x…

前端使用Ant Design中的Modal框实现长按顶部拖动弹框需求

需求&#xff1a;需要Ant Design中的一个Modal弹框&#xff0c;并且可以让用户按住顶部实现拖动操作 实现&#xff1a;在Ant Design的Modal框的基础上&#xff0c;在title中添加一个onMouseDown去记录拖拽的坐标&#xff0c;然后将其赋值给Modal的style属性 代码部分&#xff…

没有面试邀约还在怪学历?帮助1万人通过面试的简历实战经验

很多人说自己明明投了很多公司的简历&#xff0c;但是都没有得到面试邀请的机会。自己工作履历挺好的&#xff0c;但是为什么投自己感兴趣公司的简历&#xff0c;都没有面试邀请的机会。反而是那些自己没有投递的公司&#xff0c;经常给自己打电话。 造成这些现象的主要原因&am…

解决白屏问题:让你的网站重焕生机

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

Ubuntu上安装任意版本nodejs方法

在Ubuntu中安装指定版本的Node.js&#xff0c;可以使用Node Version Manager (NVM)。以下是安装步骤&#xff1a; 首先&#xff0c;安装NVM。在命令行中输入&#xff1a; curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash 这个命令会下载并…

Maya人物建模

【MAYA人物建模】超详细讲解人物嘴巴、鼻子、眼睛、耳朵、头发、帽子等布线细节&#xff0c;零基础人物头部布线教程_哔哩哔哩_bilibili 原始图像凑合用&#xff0c;视屏中截图 图像导入过程技巧总结 前视图/右视图模式下导入图形 创建图层 锁定后可以避免图片位置的移动 前视…

【Pytorch、torchvision、CUDA 各个版本对应关系以及安装指令】

Pytorch、torchvision、CUDA 各个版本对应关系以及安装指令 1、名词解释 1.1 CUDA CUDA&#xff08;Compute Unified Device Architecture&#xff09;是由NVIDIA开发的用于并行计算的平台和编程模型。CUDA旨在利用NVIDIA GPU&#xff08;图形处理单元&#xff09;的强大计算…

外包干了10天,技术退步明显···

先说一下自己的情况&#xff0c;本科生&#xff0c;通过校招进入杭州某软件公司&#xff0c;干了接近3年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试&…