《面试1v1》HashMap

news2024/9/19 21:23:19

没有人比中国人更懂 HashMap

我是 javapub,一名 Markdown 程序员从👨‍💻,八股文种子选手。

面试官:HashMap 是Java程序员用得最频繁的集合之一,可以给我简单介绍一下它的内部实现机制吗?

候选人: HashMap 是一个散列映射表,它存储的内容是键值对(key-value)映射。

面试官:那它内部具体是如何实现的呢?

候选人: HashMap 在内部实现上主要包含以下几个结构:

  • 数组:HashMap 的核心数据结构就是一个 Entry[] 数组。
  • 链表:每个数组元素是一个单链表结构的头节点,当冲突产生时,会在链表中追加元素。
  • 节点:每个链表节点包含四个字段,key, value, hash 和 next。其中 key 和 value 是映射中的键值,hash 是 key 的 hashCode,next 是指向下一个节点的指针。

面试官:为什么要选择数组和链表这两种数据结构呢?

候选人: 这是因为 HashMap 要保证高效的增删改查操作,数组和链表各自的优点可以满足这个要求:

  • 数组实现通过散列算法,可以快速定位到相应的位置,保证查询的时间复杂度为O(1)。
  • 链表在冲突发生时,通过在尾部添加节点,可以高效地进行插入操作。同时也方便进行删除操作。
    所以,HashMap通过数组实现快速查找,通过链表解决冲突,既可以保证查询效率,也可以应对散列算法产生碰撞的情况。这是它的核心优雅与高效之处。

面试官:HashMap 是非线程安全的,它有哪些线程安全的替代方案呢?

候选人: 对于线程安全的需求,可以选择以下替代方案:

  1. HashTable:HashMap 的线程安全版,内部的方法基本相同,只是进行了线程安全的同步处理。
  2. ConcurrentHashMap:Java 7 的实现使用分段锁,既保证线程安全,也不会影响性能。Java 8 使用 CAS 操作来保证并发度高的操作。
  3. Hashtable:Java 老版本中提供的 hash 表实现,线程安全,但相比于前两者性能较低。现在不建议使用。
    所以,如果不需要高并发,HashTable 是一个简单直接的替代方案;如果对性能有较高要求,推荐使用 ConcurrentHashMap。两者相比,ConcurrentHashMap 的并发度更高,性能也更佳,是当前推荐的线程安全 hash 表方案。

面试官:简单说一下 HashMap 的 put 操作过程。

候选人: HashMap 的 put(key, value) 方法大致分为以下几步:

  1. 计算key的hash值,这一步通过key的hashCode()方法计算,然后进一步处理高16位和低16位产生最终的hash值。
// 计算key的hash值 
final int hash = hash(key);
  1. 根据hash值定位数组索引,如果没有冲突就直接插入。如果产生冲突,就插入冲突链表中。
int i = indexFor(hash, table.length);
// 若i位置为空,直接新建节点添加,size增加    
if (table[i] == null) {  
    table[i] = newNode(hash, key, value, null);
} 
// 若产生冲突,将节点添加到链表尾部  
else {  
    ...
} 
  1. 如果链表长度超过TREEIFY_THRESHOLD(默认8),就把链表转换为红黑树。这一步可以提高查询效率。
// 若链表长度大于8,链表转换为红黑树    
if (binCount > TREEIFY_THRESHOLD) 
    treeifyBin(tab, hash);  
  1. 如果节点已经存在,就替换oldValue为新值。
// 若节点已存在,用新值替换旧值
for (Node<K,V> e = p;; e = e.next) {
    if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
        e.value = value;
        break;
    }
  1. 如果容量达到阈值,就进行resize两倍扩容,这一步可以减少hash冲突,提高查询性能。
if (++size > threshold) 
    resize(newCapacity); 

面试官:说说HashMap的扩容机制。

候选人: HashMap的扩容机制就是在put时,如果size已经超过阈值threshold,就会进行扩容resize操作。在扩容时,capacity的容量会扩大两倍,并会重新计算每个节点的hash值和索引位置。

面试官:为什么要进行两倍扩容?

候选人: 这是因为HashMap采用开放定址法来解决冲突,每次扩容时,原有的hash值都需要重新计算,如果扩容过小,重新计算后的索引位置有很大概率仍然会发生冲突,效果不明显。如果采用两倍扩容,然后重新计算hash值,那么冲突的概率会大大减少,查询性能就能得到较大提高。

面试官:说说resize的实现过程。

候选人: resize的实现过程主要分为以下几步:

  1. 将oldTable的值赋给newTable,newTable的长度是oldTable的两倍。
Node<K,V>[] newTable = (Node<K,V>[])new Node[newCapacity];
table = newTable;
  1. 遍历oldTable的每个桶,如果桶位非空,就重新计算每个节点的hash值和索引,并放入newTable对应的桶中。
for (int j = 0; j < oldCap; ++j) {
    Node<K,V> e;
    if ((e = oldTab[j]) != null) {
        oldTab[j] = null;
        if (e.next == null)
            newTab[e.hash & (newCap - 1)] = e;
        else ... 
    }
} 
  1. resize的过程中,如果节点的新的索引位置相同,就会在链表中追加新节点。如果不同,就在新位置形成新的链表。
else if (e instanceof TreeNode) 
    ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // 链表情况 
    Node<K,V> loHead = null, loTail = null;
    Node<K,V> hiHead = null, hiTail = null;
    Node<K,V> next;
    do {
        next = e.next;
        ...
    } while ((e = next) != null);
}
  1. 如果链表长度过长,就会先将链表转成红黑树,再进行resize。这一步可以有效提高性能。

面试官:最后,总结一下HashMap的优势。

候选人: HashMap的主要优势有:

  1. 查询和修改的时间复杂度都是O(1),这是通过哈希算法实现的。
  2. HashMap是非线程安全的,可以选择并发版的ConcurrentHashMap。
  3. HashMap通过扩容和链表转红黑树,可以动态调整容量和提高查询性能。
  4. HashMap支持null键和null值。
  5. HashMap的实现是非常巧妙的,通过数组和链表组合,既可以支持快速查找,也可以解决冲突。
  6. HashMap有很高的空间利用率,可以存储大量元素。
    所以,总之,HashMap的优势在于:性能高效,支持null,动态扩容,空间利用率高,这也是它成为Java最常用的Map实现的原因。

面试官:说说HashMap的缺点。

候选人: HashMap也存在一定的缺点:

  1. HashMap是非线程安全的。多线程环境下,需要对HashMap进行同步处理,可以选择HashTable或者ConcurrentHashMap。
  2. HashMap的迭代顺序是未定义的。每次迭代的顺序可能不同,如果需要顺序,可以采用LinkedHashMap。
  3. HashMap的遍历也是O(n)的时间复杂度,如果集合很大,遍历会很慢。可以通过提高初始容量和负载因子来减少冲突及拉链长度,提高性能。
  4. 如果Hash算法设计不当,HashMap的性能会很差。比如大量Hash冲突会导致拉链过长,严重影响查询性能。对于自定义类型作为键,需要重写hashCode()方法来保证分布均匀。
  5. HashMap采用拉链法解决冲突,極端情况下(数据全部使用相同hashCode)会退化为链表,变成O(n)的时间复杂度,查询性能变差。这种情况可以通过使用TreeMap来改进。
  6. HashMap的初始容量和扩容机制的设计不当,会造成非必要的数学开销,影响性能。
  7. HashMap不支持排序,如果需要排序可以使用TreeMap或者对HashMap进行排序。
    所以,总结来说,HashMap的主要缺点在于:非线程安全,遍历慢,迭代顺序不定,自定义键的hashCode()设计不好会导致性能下降,不支持排序等。但这些缺点都可以通过选择其他Map实现或辅助结构来补充。如果能清楚 HashMap 的这些缺点,就可以更好地选择和使用它。

面试官:你说的很全面和深入。HashMap作为一个高频使用的数据结构,你对它的理解已经相当深刻了。

候选人: 谢谢面试官的夸奖!HashMap确实是我常用的数据结构之一,我通过阅读源码、实践使用与论坛上的讨论,对它的设计和实现有了比较深入的理解。但HashMap的内容还是非常之广博,我还需要继续学习和总结,以进一步加深理解,利用好它提供的功能,并在实际工作中发挥其优势。我会持续努力,不断提高自己对这方面的认知。

面试官:很好,你对自己的提高有清醒的认识。最后,我想问你作为HashMap的替代,现在有什么其他的Map实现可以选择?

候选人: 除了HashMap,Java中常用的Map还有:

  1. Hashtable:HashMap的线程安全版本,性能差一些,现在不太建议使用。
  2. ConcurrentHashMap:支持高并发的线程安全Map,在Java8之前使用分段锁,之后使用CAS保证并发度高的操作。是HashMap的线程安全替代方案。
  3. TreeMap:基于红黑树实现,支持排序,复杂度O(logN), Keys自动排序。
  4. LinkedHashMap:内部维护着一个双向链表,结合HashMap提供按插入顺序或访问顺序遍历Map中的条目。
  5. EnumMap:键是枚举类型,内部实现更加紧凑高效。
  6. WeakHashMap:键是弱引用,容易被垃圾回收,防止内存泄露。
    所以,根据需要的功能,有多个可替代的Map实现供选择。但作为最基本和高效的实现,HashMap还是最为常用和推荐的。

面试官:很好,你的理解和应用已经相当不错了。熟练运用设计模式,在项目开发中可以 large-scale 重构,提高系统扩展性和复用性。你继续加深对各种设计模式的理解和运用,技术还会更上一层楼。

最后,你有什么问题想要提问吗?今天的面试到此结束。

候选人: 非常感谢面试官今天的时间!通过我们的交流,我对很多技术内容有了进一步的认识和提高,也清楚自己的不足和需要努力的方向。这对我来说很宝贵。
我现阶段没有其他问题了。我会继续努力学习,不断总结和实践,特别是对数据结构、算法与设计模式等基础内容的运用,提高自己的工程化水平与解决问题的能力。

再次感谢面试官!这是一次非常有价值的交流,很高兴有机会进行这样的技术探讨。谢谢!

面试官: 你的态度很好,技术也不错,继续努力深造,我相信你一定会越来越精进。这也是我作为面试官最喜欢看到的,不论最终结果如何,重要的是候选人的心态和潜力。
你也提出了很好的问题,我们进行了广泛而深入的探讨,这说明你在学习和工作中确实遇到过一定的困惑,而又积极主动寻求解决之道。这种积极主动的学习态度很难得,技术人员走的最远的,永远都是在学习和总结中不断超越自己。

很高兴今天的交流,这也使我有机会重温。

最近我在更新《面试1v1》系列文章,主要以场景化的方式,讲解我们在面试中遇到的问题,致力于让每一位工程师拿到自己心仪的offer,感兴趣可以关注JavaPub追更!

🎁目录合集:

Gitee:https://gitee.com/rodert/JavaPub

GitHub:https://github.com/Rodert/JavaPub

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

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

相关文章

C++ -3- 类和对象 (中) | 拷贝构造函数 赋值运算符重载

文章目录 4.拷贝构造函数什么是拷贝构造函数&#xff1f;应用——示例&#xff1a;日期计算器什么情况下需要自己实现拷贝构造函数&#xff1f; 5.赋值运算符重载运算符重载&#xff08;重要&#xff09;赋值运算符重载 拷贝构造函数和赋值重载函数 4.拷贝构造函数 什么是拷贝…

Baumer工业相机堡盟工业相机如何联合BGAPI SDK和OpenCV实现Mono12和Mono16格式位深度的图像保存(C++)

Baumer工业相机堡盟工业相机如何联合BGAPI SDK和OpenCV实现Mono12和Mono16位深度的图像保存&#xff08;C&#xff09; Baumer工业相机Baumer工业相机保存位深度12/16位图像的技术背景代码案例分享1&#xff1a;引用合适的类文件2&#xff1a;BGAPI SDK在图像回调中联合OpenCV保…

Effective C++条款条款42:了解typename的双重意义(Understand the two meanings of typename)

Effective C条款条款42&#xff1a;了解typename的双重意义&#xff08;Understand the two meanings of typename&#xff09; 条款42&#xff1a;了解typename的双重意义1、从属名称和非从属名称2、typename在traits机制中的运用3、牢记 总结 《Effective C》是一本轻薄短小的…

1.17 从0开始学习Unity游戏开发--场景切换

前面的所有文章我们都在一个固定的游戏场景内进行开发&#xff0c;在最开始介绍场景这个概念的时候就已经提及&#xff0c;这个场景可以是一张地图&#xff0c;或者是一个对战房间等等&#xff0c;所以显然这个场景可以有多个&#xff0c;并且可以从一个场景切换到另外一个场景…

Collection接口

文章目录 1. Java集合框架概述2. Collection接口中15个方法的使用3. Iterator(迭代器)接口4. Connection子接口一&#xff1a;List4.1 List的实现类4.2 源码分析4.2.1 ArrayList源码分析4.2.2 LinkedList源码分析4.2.3 Vector源码分析 4.3 List接口中的常用方法 5. Collection子…

死锁---银行家算法例题

1、知识点 1.银行家算法使用的四个必要的数据结构是: 可用资源向量Available&#xff0c;最大需求矩阵Max&#xff0c;分配矩阵Allocation&#xff0c;需求矩阵Need。 2.银行家算法是不是破坏了产生死锁的必要条件来达到避免死锁的目的&#xff1f;若是&#xff0c;请简述破…

【数字 IC / FPGA】 有关建立/保持时间计算的思考

引言 最近准备一些数字IC的机试&#xff0c;刷到了一些有关静态时序分析的题目。有一些比较经典的题目&#xff0c;在这里整理分享一下。 有什么疑问可以在评论区交流~互相进步 双D触发器典型电路 假设时钟周期为Tcycle,Tsetup,Thold分别为触发器建立保持时间&#xff0c;为…

Mac OS挂载ext4硬盘

一、安装macFUSE Home - macFUSE 如下载macfuse-4.4.3dmg安装 安装过程可能会遇到“若要要启用系统扩展,您需要在恢复环境中修改安全性设置”的提示&#xff0c;如下图&#xff1a; 解决&#xff1a; 关机&#xff0c;直到键盘灯全灭了&#xff01; 再按住开机键&#xff0c…

机器视觉技术分享-彩色图像处理 含c++ ,python代码说明

彩色图像处理是指对彩色图像进行数字处理和分析的过程&#xff0c;其目的是提取图像的有用信息&#xff0c;改善图像质量&#xff0c;实现图像的增强、复原、分割、匹配、识别等功能。 针对彩色图像处理&#xff0c;可以采用以下一些常见的方法&#xff1a; 1. 颜色空间转换&…

简简单单认识一下Inscode

CSDN最新推出的Inscode服务是一个在线编程工具&#xff0c;旨在为开发者提供一个便捷的编写、运行和分享代码的环境&#xff0c;让开发者无需在本地搭建编程环境&#xff0c;即可快速编写和运行代码。 Inscode支持多种编程语言&#xff0c;包括Java、Python、C等&#xff0c;同…

C语言进阶之回调函数详解分析方法

一、函数指针 在讲回调函数之前&#xff0c;我们需要了解函数指针。 我们都知道&#xff0c;C语言的灵魂是指针&#xff0c;我们经常使用整型指针&#xff0c;字符串指针&#xff0c;结构体指针等。 int *p1; char *p2; STRUCT *p3; // STRUCT为我们定义的结构体 但是好像我…

PlumGPT【告别梯子,拥抱AI】

相信很多人苦于没有openai账号或者有着种种原因至今还没有使用过chatgpt&#xff0c;今天向大家推荐一个网站&#xff0c;在国内也可以任意方便使用&#xff0c;让你的办公效率最大化。 那就是PlumGPT&#xff1a;https://plumgpt.com/ PlumGPT&#xff08;国内版的chatgpt&a…

Mybatis分页实现

1. Rowbounds Rowbounds将所有符合条件的数据加载到内存&#xff0c;然后再实现逻辑切割。 Override public List<User> getAllUser() {RowBounds rowBounds new RowBounds(1, 2);return userMapper.getAllUser(rowBounds); }查询sql&#xff08;没有任何分页逻辑&…

【Redis】常用命令、各种数据结构及命令

目录 一、常见数据结构 二、常用命令 1、查询符合的所有key 2、删除key 3、判断key是否存在 4、给key设置过期时间 5、查看key的剩余过期时间 三、不同数据类型的操作命令 1、String 1.set 2.get 3.mset 4.mget 5.incr 6.incrby 7.incrbyfloat 8.setnx 9.se…

C++——内存分配与动态内存管理

文章目录&#x1f490;专栏导读&#x1f490;文章导读&#x1f337;C/C内存分布&#x1f33a;牛刀小试&#x1f33a;C语言动态内存管理&#x1f337;C动态内存管理&#x1f33a;对于内置类型&#x1f33a;对于自定义类型&#x1f337;operator new与operator delete函数&#x…

便携式明渠流量计有哪几种呢?

便携式明渠流量计有几种&#xff1f; 目前来说市面上是有两种&#xff0c;但最终的作用或者说是功能都是用来和明渠在线流量计做液位和流量比对的一种装置。 这两种有什么区别呢&#xff1f; 一种就是便携式明渠流量计磁致伸缩流量计&#xff0c;另一种就是便携式明渠超声波…

浅析EasyCVR基于B/S架构的技术特点与能力应用

EasyCVR基于云边端协同&#xff0c;可支持海量视频的轻量化接入与汇聚管理。平台兼容性强、拓展度高&#xff0c;可提供视频监控直播、视频轮播、视频录像、云存储、回放与检索、智能告警、服务器集群、语音对讲、云台控制、电子地图、平台级联等功能。 EasyCVR视频融合平台采用…

【MyBatis Plus】004 -- MyBatis Plus高级(AR、MP插件、自定义全局操作、自动填充、逻辑删除、枚举、代码生成器)

目录 1、ActiveRecord 1.1 开启AR之旅&#xff08;根据主键 id 进行查询&#xff09; 1.2 新增数据 1.3 更新操作 1.4 删除操作 1.5 根据条件查询 2、Oracle 主键 Sequence 2.1 部署Oracle环境 2.2 创建表以及序列 2.3 jdbc驱动包 2.4 修改application.properties 2.5 配置序列…

LC-1041 困于环中的机器人(模拟,快慢指针找环)

1041. 困于环中的机器人 难度中等148 在无限的平面上&#xff0c;机器人最初位于 (0, 0) 处&#xff0c;面朝北方。注意: 北方向 是y轴的正方向。南方向 是y轴的负方向。东方向 是x轴的正方向。西方向 是x轴的负方向。 机器人可以接受下列三条指令之一&#xff1a; "…

第一讲 初识Python

Python简介 Python&#xff08;英式发音&#xff1a;/ˈpaɪθən/&#xff1b;美式发音&#xff1a;/ˈpaɪθɑːn/&#xff09;是由荷兰人吉多范罗苏姆&#xff08;Guido von Rossum&#xff09;发明的一种编程语言&#xff0c;是目前世界上最受欢迎和拥有最多用户群体的编…