Java HashMap原理解析

HashMap是Java中常用的一种数据结构,主要用于存储键值对。它基于哈希表实现,其中键和值都可以为null。HashMap有着优秀的插入和查找效率,因此得到了广泛的应用。本文将对Java HashMap的具体实现进行详细阐述,包括哈希表的结构、扩容机制、实现原理以及在Java8中的优化。

一、哈希表的结构

HashMap底层是一个“链表散列”的结构,即数组和链表的结合体。具体而言,HashMap底层是一个数组(table数组),数组中的每个元素称为桶(bucket),每个桶中又存放着链表。当键值对被put进Hashmap中时,通过hash函数计算出对应的hash值(即桶的index),然后将键值对加入到对应桶的链表中。

下面是一个简单的HashMap示意图:

             +----+    +----+
    table[0] |    | -> |    |
             +----+    +----+
    table[1] |    | -> |    |
             +----+    +----+
          ... 
             +----+    +----+             +----+
    table[n] |    | -> |    | -> ... -> |    |
             +----+    +----+             +----+

上图中table数组长度为n,每个元素是一个链表的头结点。每个链表头节点代表桶,对应的链表中存储了hash值相同的键值对。

二、扩容机制

当HashMap中存储的键值对数量达到一定程度时(即超过了容量*负载因子),HashMap就需要对table数组进行扩容,以保证存储空间的充足。

Java8之前的默认负载因子为0.75,意味着table数组的容量为n时,最多可存放n*0.75个键值对。当所存储的键值对数量即将超过容量时,HashMap会将table数组的长度扩大为原来的两倍,即将容量翻倍,然后将原来的键值对重新哈希到新数组中。

下图是HashMap扩容前后的对比示意图:

    // table长度为4,负载因子为0.75,最多存储3个键值对
                    +--------------+
                    |              |
                    v              |
    +------+------+------+------+
    | node | node | null | null |
    +------+------+------+------+

    // 添加第4个键值对时,HashMap扩容,将table长度扩大为8
                           +------------------------+
                           |                        |
                           v                        |
    +------+------+------+------+------+------+------+------+ 
    | node | node | null | null | null | null | null | null | 
    +------+------+------+------+------+------+------+------+

扩容时,原来的键值对仍然存储在原有的桶对应的链表中,而新添加的键值对则可能会散落到新的桶链表中。

三、实现原理

HashMap是一个典型的哈希表,它本质上是通过哈希函数将键映射到桶的位置,然后将键值对存储在对应桶的链表中。HashMap中使用的哈希表具有以下特点:

  • 哈希表桶数量不固定,动态扩容;
  • 哈希函数计算尽可能地减少哈希冲突发生的概率;
  • 键值对存储方式为链表;
  • 链表长度较长时,自动转化为红黑树,以提高查找效率;
  • 键和值均可为null。

根据Java8的源码,HashMap的哈希函数实现如下:

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

可以看出,哈希函数主要由两个步骤组成:获取键的hashCode,然后通过位运算(异或运算和无符号右移操作)将高位的影响降低,使得得到的hash值更加乱序。

对于哈希冲突的处理方式,HashMap采用的是拉链法。即当出现哈希冲突时,将键值对添加至对应桶的链表中,之后根据键的equals方法找到对应的节点,即可获取或修改对应的值。

在Java8中,为了提高HashMap的查找效率,引入了红黑树的优化。当桶的长度大于等于8时,将链表转化为平衡树。通过红黑树的查找方式,可以将查找效率提高至O(log n)。

四、在Java8中的优化

在Java8中,HashMap进行了大量的优化,主要有以下几点:

  • 哈希表的长度不再以2的整 数次幂为基础,而是使用保持2的整数次幂的最小值的方法来分配桶的数量。
  • 桶的数量很少,因为HashMap使用了红黑树,这样链表很长的目标性从O(n)变成了O(log n)。
  • 引入了TreeNode,当桶中链表的长度超过8时,链表会转化为红黑树,以提高查找效率。
  • 当链表的长度小于等于6时,会转为链表,避免了因为节点太少转换为树而带来的额外开销。
  • 在遍历HashMap时,使用了迭代器,使得在遍历时可以通过指向后一个元素的指针,快速进行下一轮迭代。

下面是Java8中红黑树的示意图:

    +-------------+
    |             |
    v             |
  +------+     +------+
  | node |---->| node |--->...
  +------+     +------+
             |     |
             v     v
           +-----+-----+
           | 黑(bo) | 黑(bo) |
           +-----+-----+
             |     |
             v     v
           +------+------+
           | node | node |
           +------+------+
           |      |      |
           v      v      v
           .      .      .
           .      .      .
           .      .      .

五、代码演示

以下代码演示了如何创建HashMap,添加键值对和遍历HashMap:

import java.util.*;

public class HashMapDemo {
    public static void main(String[] args) {
        // 创建HashMap并添加键值对
        Map map = new HashMap();
        map.put("apple", 1);
        map.put("banana", 2);
        map.put("orange", 3);

        // 遍历HashMap
        for (Map.Entry entry : map.entrySet()) {
            System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
        }
    }
}

输出:

Key: orange, Value: 3
Key: banana, Value: 2
Key: apple, Value: 1

六、总结

本文对Java HashMap进行了详细的阐述,包括哈希表的结构、扩容机制、实现原理以及在Java8中的优化。HashMap是Java中非常重要的数据结构之一,在实际开发中经常用来存储大量的键值对。熟悉HashMap的实现原理,有助于我们更好地理解其使用方法,避免出现一些隐晦的问题。

原创文章,作者:小蓝,如若转载,请注明出处:https://www.506064.com/n/199212.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
小蓝小蓝
上一篇 2024-12-04 19:15
下一篇 2024-12-04 19:15

相关推荐

  • Java JsonPath 效率优化指南

    本篇文章将深入探讨Java JsonPath的效率问题,并提供一些优化方案。 一、JsonPath 简介 JsonPath是一个可用于从JSON数据中获取信息的库。它提供了一种DS…

    编程 2025-04-29
  • java client.getacsresponse 编译报错解决方法

    java client.getacsresponse 编译报错是Java编程过程中常见的错误,常见的原因是代码的语法错误、类库依赖问题和编译环境的配置问题。下面将从多个方面进行分析…

    编程 2025-04-29
  • Java腾讯云音视频对接

    本文旨在从多个方面详细阐述Java腾讯云音视频对接,提供完整的代码示例。 一、腾讯云音视频介绍 腾讯云音视频服务(Cloud Tencent Real-Time Communica…

    编程 2025-04-29
  • Java Bean加载过程

    Java Bean加载过程涉及到类加载器、反射机制和Java虚拟机的执行过程。在本文中,将从这三个方面详细阐述Java Bean加载的过程。 一、类加载器 类加载器是Java虚拟机…

    编程 2025-04-29
  • Java Milvus SearchParam withoutFields用法介绍

    本文将详细介绍Java Milvus SearchParam withoutFields的相关知识和用法。 一、什么是Java Milvus SearchParam without…

    编程 2025-04-29
  • Java 8中某一周的周一

    Java 8是Java语言中的一个版本,于2014年3月18日发布。本文将从多个方面对Java 8中某一周的周一进行详细的阐述。 一、数组处理 Java 8新特性之一是Stream…

    编程 2025-04-29
  • Java判断字符串是否存在多个

    本文将从以下几个方面详细阐述如何使用Java判断一个字符串中是否存在多个指定字符: 一、字符串遍历 字符串是Java编程中非常重要的一种数据类型。要判断字符串中是否存在多个指定字符…

    编程 2025-04-29
  • VSCode为什么无法运行Java

    解答:VSCode无法运行Java是因为默认情况下,VSCode并没有集成Java运行环境,需要手动添加Java运行环境或安装相关插件才能实现Java代码的编写、调试和运行。 一、…

    编程 2025-04-29
  • Java任务下发回滚系统的设计与实现

    本文将介绍一个Java任务下发回滚系统的设计与实现。该系统可以用于执行复杂的任务,包括可回滚的任务,及时恢复任务失败前的状态。系统使用Java语言进行开发,可以支持多种类型的任务。…

    编程 2025-04-29
  • Harris角点检测算法原理与实现

    本文将从多个方面对Harris角点检测算法进行详细的阐述,包括算法原理、实现步骤、代码实现等。 一、Harris角点检测算法原理 Harris角点检测算法是一种经典的计算机视觉算法…

    编程 2025-04-29

发表回复

登录后才能评论