首页 > 其他分享 >几个关于HashMap超经典的面试题! 欢迎补充

几个关于HashMap超经典的面试题! 欢迎补充

时间:2024-07-19 14:56:31浏览次数:18  
标签:面试题 HashMap 数组 链表 键值 哈希 经典 null

HashMap 是 Java 中一个非常重要且常用的数据结构,因此在技术面试中经常会被问到。以下列出几个经典的关于 HashMap 的面试题,并给出详细的答案和解释。

1:HashMap 的工作原理是什么?

回答
HashMap 是基于哈希表的一种数据结构,它存储键值对(key-value pair),并通过键的哈希码将值存储到一个数组中。

基本工作原理

  1. 哈希值计算:当一个键值对插入到 HashMap 中时,首先会计算键的哈希值(使用 hashCode 方法)。
  2. 定位桶:根据哈希值确定该键值对在数组中的位置(桶/桶索引)。这个位置通常通过对数组长度取模(hash % n,这里 n 是数组长度)来确定。
  3. 冲突解决:如果多个键的哈希值经过取模运算落在了相同的桶中,就会出现哈希冲突。HashMap 采用链地址法来解决冲突,即在桶中使用链表或者红黑树存储多个键值对。
  4. 扩容:当插入的元素数量超过数组容量(负载因子)的某个阈值时,HashMap 会进行扩容(通常是两倍扩容)。扩容时,需要重新计算每个现有键值对的哈希值并移动到新的桶中。

JDK 1.8 以后的改进

  • JDK 1.8 采用红黑树优化链表。当链表长度超过 8 且数组长度超过 64 时,链表会转换为红黑树,以提高插入和查找的效率。

2:什么是 Hash 冲突?HashMap 如何处理 Hash 冲突?

回答
Hash 冲突:当两个或多个键的哈希值在经过取模运算后映射到 HashMap 的同一个桶位置时,发生了哈希冲突。

HashMap 处理哈希冲突的方法

  1. 链地址法:将具有相同哈希值的键值对存储在一个链表中。链表的头部存储在桶中,链表节点存储相应的键值对。

    示例

    // 简化的链地址法示例
    class Node {
        int key;
        int value;
        Node next;
        Node(int key, int value) { this.key = key; this.value = value; }
    }
    
  2. 红黑树:在 JDK 1.8 之后,当链表长度超过 8 且 HashMap 数组长度大于 64 时,链表会转换为红黑树。红黑树具有平衡二叉树的性质,能够在 O(log n) 时间复杂度内进行插入和查找操作。

3:HashMap 的扩容过程是怎样的?

回答
HashMap 的扩容是当当前容量超过阈值(capacity * loadFactor)时触发的。

扩容过程

  1. 创建新数组:扩容时创建一个容量是原来数组两倍的新数组。
  2. 重新 hash:将原数组中的每个键值对重新计算哈希值,并根据新的数组长度确定新的桶位置。
  3. 桶中的元素重新分配:将原链表或树中存储的元素重新分配到新数组的桶中。

示例代码

// Simplified resize method from HashMap class
void resize() {
    Node<K,V>[] oldTable = table;
    int oldCapacity = oldTable.length;
    int newCapacity = oldCapacity << 1;  // Double the capacity
    Node<K,V>[] newTable = new Node[newCapacity];
    
    for (int i = 0; i < oldCapacity; i++) {
        Node<K,V> e;
        if ((e = oldTable[i]) != null) {
            oldTable[i] = null;  // Help GC
            if (e.next == null) {
                newTable[e.hash & (newCapacity - 1)] = e;  // Single element in bucket, move it directly
            } else if (e instanceof TreeNode) {
                // Handle tree nodes…
            } else {
                // Rehash all elements in the same bucket
                Node<K,V> loHead = null, loTail = null;
                Node<K,V> hiHead = null, hiTail = null;
                Node<K,V> next;
                do {
                    next = e.next;
                    if ((e.hash & oldCapacity) == 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);
                if (loHead != null) newTable[i] = loHead;
                if (hiHead != null) newTable[i + oldCapacity] = hiHead;
            }
        }
    }
    table = newTable;
}

4:HashMap 和 ConcurrentHashMap 的区别?

回答
HashMapConcurrentHashMap 都是存储键值对的集合,但它们在并发环境下的表现不同。

  1. 线程安全性

    • HashMap:非线程安全的。多个线程同时对 HashMap 进行读写操作可能导致数据不一致。
    • ConcurrentHashMap:线程安全的。通过分段锁或 CAS 操作实现高效并发控制。
  2. 锁机制

    • HashMap:没有任何锁机制。
    • ConcurrentHashMap:JDK 1.7 采用分段锁(Segment)。JDK 1.8 取消分段锁,采用 CAS 和 synchronized 锁优化并发控制。
  3. 性能

    • HashMap:在没有并发需求的情况下,性能较好。
    • ConcurrentHashMap:在并发较高的场景下,性能优越。
  4. 迭代器

    • HashMap:迭代器是快速失败的(fail-fast)。如果在迭代过程中检测到集合被修改,会抛出 ConcurrentModificationException
    • ConcurrentHashMap:迭代器是弱一致性的,不抛异常,只能保证在创建迭代器时的元素一致性,后续元素变动不影响当前迭代结果。

5:为什么 HashMap 的 initialCapacity 应该是 2 的幂?

回答
HashMap 的 initialCapacity 应该是 2 的幂次主要是为了优化哈希分布,减少哈希冲突。具体原因如下:

  1. 提高哈希分布均匀性:使用 hash % n 来确定元素存放的桶位置。n 为 2 的幂时,n-1 的二进制表示形式只有低位是 1,高位全是 0,这样进行 hash & (n-1) 操作时,高位信息不会被丢失,提高了哈希分布的均匀性。

    示例

    int hash = key.hashCode();
    int index = hash & (n - 1); // n is a power of 2
    
  2. 提高计算效率:使用 &(按位与)操作比 %(取模)操作更快,因为 & 是位操作,直接在二进制层面进行。

  3. 避免resize过程中的浪费:当容量是 2 的幂时,resize 过程中的元素会重新分布到新的桶位置上,不会产生严重的哈希冲突,有助于保持均匀分布。

综上所述,选择 2 的幂次作为 HashMap 的初始容量有助于提高哈希分布的均匀性和插入、查询的性能。

标签:面试题,HashMap,数组,链表,键值,哈希,经典,null
From: https://blog.csdn.net/qq_40592590/article/details/140526036

相关文章

  • C语言面试题
    C语言面试题通常涵盖了C语言的各种概念和技术,从基础知识到高级主题都有可能涉及。以下是一些常见的C语言面试题示例,这些问题可以帮助你准备面试,无论是针对初级还是高级程序员:基础知识C语言的预处理器做了什么?描述预处理器的工作,包括宏定义、条件编译和头文件包含。解......
  • Java基础常见面试题总结(下)
    目录异常Exception和Error有什么区别?Throwable类常用方法有哪些?  try-catch-finally如何使用?finally代码块中的代码一定会执行吗? 异常使用有哪些需要注意的地方? 泛型什么是泛型?有什么作用?泛型的使用类型有哪几种?项目中哪里使用到了泛型?反射反射是什么? ......
  • java map 是线程安全吗 map的线程安全实现类 推荐使用 ConcurrentHashMap
    javamap是线程安全吗map的线程安全实现类推荐使用ConcurrentHashMapHashMap线程安全的吗?Java中平时用的最多的Map集合就是HashMap了,它是线程不安全的。看下面两个场景:1、当用在方法内的局部变量时,局部变量属于当前线程级别的变量,其他线程访问不了,所以这时也不存在线程安全......
  • Java面试题系列 - 第16天
    题目:Java中的日期和时间API背景说明:Java中的日期和时间API经历了几次重大变革,从最初的基本Date和Calendar类,到Java8中引入的现代日期时间API(java.time包),提供了更强大、更直观的时间处理能力。掌握现代日期时间API的使用,对于编写准确和可维护的日期时间相关代码至关重要。问......
  • 【Qt】探索Qt框架:开发经典贪吃蛇游戏的全过程与实践
    文章目录引言项目链接:1.Qt框架的使用简介2.贪吃蛇游戏设计2.1游戏规则和玩法介绍2.2游戏界面设计概述3.核心代码解析3.1主界面(GameHall)3.1.1布局和功能介绍3.1.2代码实现分析3.2游戏选择界面(GameSelect)3.2.1功能介绍3.2.2代码实现分析3.3游戏房间(GameRoom......
  • 【数学建模】——多领域资源优化中的创新应用-六大经典问题解答
    目录题目1:截取条材题目 1.1问题描述1.2数学模型1.3求解1.4解答题目2:商店进货销售计划题目2.1问题描述2.2数学模型2.3求解2.4解答题目3:货船装载问题题目3.1问题重述 3.2数学模型3.3求解3.4解答题目4:城市消防站选址问题 题目4.1问题重述4.2......
  • 【车载测试面试:各大车企面试题汇总】
            HIL(硬件在环)测试、UDS功能诊断、UDS自动化诊断、数据库制作、DTC故障制造、CANoe工具使用、ECU刷写、报文解析、导航测试、车控测试、OTA升级测试、TBOX测试等TBOX 深圳 涉及过T-BOX测试吗Ota升级涉及的台架环境是什么样的?上车实测之前有没有一个......
  • 【面试题】MVCC多版本并发控制
    多版本并发控制指的就是维护一个数据的多个版本,,是得读写操作没有冲突;MVCC和锁(排他锁)也是事务隔离性的保证就好比以下的例子,我们查询id为30的记录到底是查询的是哪个事务所有修改的数据呢?这个就是MVCC的特点了,MVCC可保证我们读写操作没有冲突MVCC的具体实现,主要是依赖于数据......
  • Java二叉树经典例题
    目录一.相同的树二.翻转二叉树三.平衡二叉树四.对称二叉树五.根据前(后)和中序排序构建二叉树1.前序和中序构建2.后序和中序构建六.二叉树的层序遍历七.二叉树非递归遍历1.前序遍历2.中序遍历3.后序遍历八.总结前言:前面说过树是通过递归定义的。做二叉树的题,递......
  • 甲骨文面试题【动态规划】力扣377.组合总和IV
    给你一个由不同整数组成的数组nums,和一个目标整数target。请你从nums中找出并返回总和为target的元素组合的个数。题目数据保证答案符合32位整数范围。示例1:输入:nums=[1,2,3],target=4输出:7解释:所有可能的组合为:(1,1,1,1)(1,1,2)(1,2,1)......