HashMap的面试题

news2025/7/10 10:23:32

目录

1、底层数据结构 1.7和1.8有何不同

2、为什么用红黑树,为何不一上来就树化,树化阈值为何是8,何时会树化,何时会退化为链表

3、索引如何计算?hashCode都有了,为何还要提供hash()方法?数组容量为何是2的n次幂

4、HashMap的put方法流程 1.7和1.8有何不同

5、加载因子为何默认是0.75

6、多线程下操作HashMap(线程不安全的)会有什么问题

7、key是否能为null,作为key的对象有什么要求?

8、String对象的hashCode()如何设计的,为啥每次乘的是31


1、底层数据结构 1.7和1.8有何不同

1.7 数组+链表

1.8 数组+链表+红黑树

2、为什么用红黑树,为何不一上来就树化,树化阈值为何是8,何时会树化,何时会退化为链表

① 红黑树是用来避免DOS攻击,防止链表超长时性能下降,树化应该是偶然情况 1.hash表的查询,时间复杂度是O(1),而红黑树的查找,时间复杂度是O(log2(n)),并且树化占用的空间也普遍比链表大,如非必要,尽量还是使用链表 2.hash值如果只够水机,则在hash表内按照泊松分布,在负载因子是0.75的情况下,长度超过8的链表出现的概率是0.00000006,选择8就是为了让树化的几率足够小

② 树化的两个条件 链表长度大于8 数组容量大于等于64

③ 退化链表的两种情况 1.在扩容的时候,如果拆分树时,树元素个数<=6 则会退化链表(如果是7的话,会频繁进行红黑树和链表的转换,影响性能,所以退了一位,使得不那么容易变回红黑树) 2.remove树结点的时候,如果root,root.left,root.right,root.left.left有一个为null,也会退化为链表

3、索引如何计算?hashCode都有了,为何还要提供hash()方法?数组容量为何是2的n次幂

① 首先计算对象的hashCode(),在调用HashMap的hash()方法进行二次哈希,最后 &(capacity-1)得到索引

// key就是hashCode()的值
  public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
  //hash(key) 是进行二次哈希
  static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
  // n是capacity 数组容量   hahs是二次哈希得到的值
   p = tab[i = (n - 1) & hash]

② 二次哈希 hash() 是为了综合高位数据,让哈希分布更均匀,防止哈希冲突,使得计算索引的时候,出现相同的概率降低防止出现链表过长的情况

1. HashMap容量较小而hash值比较大的时候,哈希冲突容易变多

2. 假设容量为16,那么二进制(0000 1111)进行按位与操作,hash值的高28位不会参与(因为0000 1111前的28位都是0,32位,其中4位符号),所以哈希冲突就会变多

3. 进行右移获取的hash值就会让二进制的高位尽可能多地参与按位与操作,从而减少哈希冲突

③ 计算索引的时候 如果是2的n次幂

好处

1.可以使用位运算代替求模运行

(求索引位置是 hash&容量 变成(容量-1)& hash), 因为 &的效率是大于/

2.扩容的时候hash& oldCap == 0 元素就留在原来位置

否则新位置= 旧位置 + oldCap

//扩容的方法中  resize()中 每次都判断
if (oldTab != null) {
            for (int j = 0; j < oldCap; ++j) {
                    else { // preserve order //维持排序
                        Node<K,V> loHead = null, loTail = null;
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;
                        do {
                            next = e.next;
                            //判断hash& oldCap == 0
                            if ((e.hash & oldCap) == 0) {
                                if (loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }
                            //否则
                            else {
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                        //hash& oldCap == 0 loTail 不等于空
                        //保持原来位置
                        if (loTail != null) {
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        //否则hiTail 不等于空  新位置 = 原来位置+oldCap(原来的容量)
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }

缺点

当例如都是偶数的时候,在如何摸容量 都索引位置都不是奇数,造成哈希表分布性不好

因此 如果追求性能就使用2的n次幂 如果追求哈希表的分布性就使用质数容量

①②③都是配合容量为2的n次幂的优化手段,但是并不能说那种设计更优,应该是设计者综合了各种因素,最终选择了2的n次幂作为容量(HashTable就是不是2的n次幂 扩容长度原来二倍+1)

4、HashMap的put方法流程 1.7和1.8有何不同

  1. HashMap是懒惰创建数组的,首次使用put方法才创建数组(创建对象的时候数组是为空的)

  2. 计算索引(桶下标)

  3. 如果桶的下标还没人占用,创建Node占位返回

  4. 如果桶下标已经有人占用了

    1. 已经是红黑树走红黑树的添加或者更新逻辑

    2. 普通的链表,走链表的添加或者更新逻辑,如果链表长度超过树化的阈值(8) ,数组容量大于等于64就进行树化逻辑

    3. 其中添加和更新逻辑,是看equals是否相等,相等就是更新逻辑,不相等就是添加逻辑

  5. 返回前检查容量是否超过阈值(容量*0.75),一旦超过进行扩容

  6. 不同

    1. 链表的插入节点,1.7是头插法,1.8是尾插法

    2. 1.7是大于等于阈值且没有空位(就是计算的索引位置有元素)的时候才扩容,而1.8是大于阈值就扩容

    3. 1.8扩容计算Node索引的时候,会优化

5、加载因子为何默认是0.75

  1. 在空间占用与查询时间之间取得较好的权衡

  2. 大于这个值,空间节省了,但是链表长度就会比较长影响性能

  3. 小于这个值,冲突减少了,但是扩容就会更加频繁,空间占用多

6、多线程下操作HashMap(线程不安全的)会有什么问题

①扩容死链(1.7)

多线程的情况会出现这种死链的情况

数组是固定长度,链表太长就需要扩充数组长度进行rehash减少链表长度。如果两个线程同时触发扩容,在移动节点时会导致一个链表中的2个节点相互引用,从而生成环链表

 

②数据错乱(1.7 1.8)

当进行添加的时候

 

线程1添加 a 取得索引为1 线程2添加1 取得索引也为1

例如线程1 进入到630行 执行为null,准备执行631的时候

线程2 进入630行执行也为null 执行631行,结束后 数组索引为1的位置元素为1

然后线程1这个时候已经if 判断完毕了,也执行631行,这个时候就将a放入到数组索引为1的位置

最终出现数据的丢失问题

7、key是否能为null,作为key的对象有什么要求?

  1. HashMap的key可以为null,但是Map的其他实现则不然,会出现空指针异常

  2. 作为key对象,必须实现hashCode和equals ,并且key的内容不能修改(不可变)

    否则修改后hashCode改变了,索引位置改变了,就会出现不同的情况

8、String对象的hashCode()如何设计的,为啥每次乘的是31

public int hashCode() {
        int var1 = this.hash;
        if (var1 == 0 && this.value.length > 0) {
            char[] var2 = this.value;

            for(int var3 = 0; var3 < this.value.length; ++var3) {
                var1 = 31 * var1 + var2[var3];
            }

            this.hash = var1;
        }

        return var1;
    }

 

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

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

相关文章

综合实验——高级网络应用检测

作者简介&#xff1a;一名在校云计算网络运维学生、每天分享网络运维的学习经验、和学习笔记。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​​ 目录 前言 综合实验 实验要求 三层交换机配置 三层交换机一 三层交换机…

教程详解从照片到倾斜摄影模型、影像地形点云的成果输出,网页端Web发布展示

一、数据获取 需要自己在下面网址中下载相应数据&#xff0c;获取the island of Torbjrnskjr&#xff0c;下载后&#xff0c;共计200张带pos的jpg照片&#xff0c;存储到纯英文目录下 国外Sensefly共享的航拍数据 包括数据原片、分辨率介绍、覆盖范围&#xff0c;飞行高度、照…

Linux-实操篇8-shell脚本编写

一、shell是什么 二、shell脚本如何执行 2.1 新建一个shell脚本 ## 第一步新建一个shell脚本 vim aaa.sh ## 内容如下&#xff0c;#!/bin/bash 表示shell脚本的执行协议&#xff0c;必须要写 #!/bin/bash echo "hello word!!!"2.2 执行方式 方式一&#xff1a;先给…

R语言中的函数19:openxlsx::read.xlsx(), write.xlsx(), writeData(), writeDataTable()

文章目录read.xlsx()函数介绍实例writeData()和writeDataTable()函数介绍实例write.xlsx()函数介绍实例read.xlsx()函数介绍 read.xlsx(xlsxFile,sheet,startRow 1,colNames TRUE,rowNames FALSE,detectDates FALSE,skipEmptyRows TRUE,skipEmptyCols TRUE,rows NULL,c…

Oracle技术分享 卸载grid软件

如果grid软件安装失败&#xff0c;可能需要重新安装&#xff0c;这时候紧紧删除软件是解决不了问题的&#xff0c;还需要删除grid的配置信息&#xff0c;需要安装软件的原因各式各样。 1 资源无法启动。 2 root.sh执行失败。 1 如果执行root.sh失败&#xff0c;可以删除&#x…

相似度系列8:unify-BARTSCORE: Evaluating Generated Text as Text Generation

BARTSCORE: Evaluating Generated Text as Text Generation 这篇文章是用生成模型解决问题&#xff0c;根据生成模型中输入和输出的差别&#xff0c;代表不同的评测方面。 不足&#xff1a;针对不同的任务选择bart score的输入和输出&#xff1f;different input and output co…

Allegro 172版本自动放置层叠

Allegro 172版本自动放置层叠 Allegro 172版本支持自动放置层叠,无需手动绘制,效果如下图 具体操作步骤如下 选择Manufacture-选择Cross Section Chart命令 会出现一个对话框 常用参数介绍如下 Chart Unit 是层叠单位 Maximun Chart height 是层叠的高度 X-Scale Factor…

预约挂号项目之预约挂号模块

目录一、预约挂号详情1、需求分析2、api接口2.1 、controller代码&#xff1a;2.2 、Service类接口&#xff1a;2.3 、添加service接口实现&#xff1a;学习指南&#xff1a; https://www.zhihu.com/question/351439302/answer/2362637429?utm_id0 一、预约挂号详情 1、需求…

[数据结构]链表OJ题 (三) 链表的中间结点、链表中倒数第k个结点、合并两个有序链表、链表分割、链表的回文结构

作者&#xff1a; 华丞臧. 专栏&#xff1a;【数据结构】 各位读者老爷如果觉得博主写的不错&#xff0c;请诸位多多支持(点赞收藏关注)。如果有错误的地方&#xff0c;欢迎在评论区指出。 推荐一款刷题网站 &#x1f449; LeetCode刷题网站 文章目录一、链表的中间结点题目描述…

Python学习笔记(十六)——Numpy

Numpy NumPy&#xff08;Numerical Python的简称&#xff09;是高性能科学计算和数据分析的基础包&#xff0c; 其中包含了数组对象(向量、矩阵、图像等)以及线性代数等。 NumPy库主要功能 • ndarray(数组)是具有矢量算术运算和复杂广播能力的多维数组。 • 具有用于对数组数…

项目资源管理从学会向上管理开始

“如何一句话证明你当过项目经理&#xff1f;” 这个话题在网上引发了广大项目管理人的兴趣&#xff0c;纷纷发表了个人看法&#xff08;变相吐槽&#xff09;。各种回答戳中笑点&#xff0c;同时也表达了作为项目经理的心酸。  “普通的薪资水平&#xff0c;却要为整个项目的…

基于微信公众平台API的菜谱小程序 的设计与实现

摘 要 由于人们生活水平的不断提高&#xff0c;人们对网络的需求也是不断提高&#xff0c;但是又不想通过下载各种不常用的app增加手机内存。小程序正好就可以做到这个特点&#xff0c;不用下载、及实际用、用完就走&#xff0c;现在很多的行业兴起&#xff0c;尤其餐饮行业最…

D. Decrease the Sum of Digits

Problem - 1409D - Codeforces 题意: 你得到了一个正整数n。在一次移动中&#xff0c;你可以使n增加1&#xff08;即使n:n1&#xff09;。你的任务是找出为了使n的数位之和小于或等于s&#xff0c;你需要执行的最小移动数。 你必须回答t个独立的测试案例。 输入 输入的第一…

基于最小二乘支持向量机(LS-SVM)进行分类、函数估计、时间序列预测和无监督学习(Matlab代码实现)

&#x1f4dd;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;…

Webpack基础使用 + 高级配置【重点!】

http://xxpromise.gitee.io/webpack5-docs/senior/optimizePerformance.html#code-split 笔记好评&#xff01;&#xff01;&#xff01;&#xff01;一定要把网址记好&#xff0c;真的nice&#xff01;&#xff01;&#xff01;&#xff01; 只是发博客记录一下&#xff0c;没…

java后端pageHelper分页实现方法

文章目录背景方法一&#xff1a;mysql的limit进行分页方法二&#xff1a;使用插件Mybatis-PageHelper(拦截器原理)1、本质2.实现步骤引入依赖修改application.yml修改代码总结背景 当一次查库数据量较大&#xff0c;不光给数据库带来压力&#xff0c;同时前端渲染页面压力也很…

Linux权限

系列文章目录 Linux 环境搭建以及xshell远程连接_crazy_xieyi的博客-CSDN博客 Linux常用命令详解_crazy_xieyi的博客-CSDN博客 文章目录 一、用户操作二、三种角色三、文件类型和访问权限四、修改文件权限一、用户操作 Linux下有两种用户&#xff1a;超级用户&#xff08;roo…

Java - SpringBoot整合Shiro之缓存功能

Java - SpringBoot整合Shiro之缓存功能前言一. SpringBoot 整合Redis1.1 配置 RedisTemplate1.2 Shiro整合Redis缓存配置1.3 测试前言 在 上一篇文章 主要讲了Shiro权限授权和认证跳过。本篇文章就主要讲解如何整合Shiro和Redis。这样就避免携带同一个Token的时候&#xff0c;…

百趣代谢组学文献分享:真假肥胖?代谢组说了算

肥胖是当今社会面临的很普遍的健康问题之一&#xff0c;超重会显著增加患糖尿病和心血管疾病的风险。而在日常生活中&#xff0c;我们发现有些肥胖者健康状况良好&#xff0c;而有些相对较瘦的人&#xff0c;却存在患糖尿病和心血管疾病的风险。 百趣代谢组学文献分享&#xf…

基于STM32结合CubeMX学习Free-RT-OS的源码之信号量与互斥量

目录 CUBEMX上的配置以及使用 信号量 互斥量 CUBEMX上的配置以及使用 信号量与互斥量都是从队列中衍生出来的&#xff0c;他们是一种特殊的队列。不同的地方在于:他们不含有队列的数据部分&#xff0c;只有队列结构体。 定义属性&#xff08;这里只有一个名字&#xff09;和…