热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

JavaHashMap实现原理分析(一)

这篇文章主要介绍了JavaHashMap实现原理的分析,帮助大家更好的理解和使用Java,感兴趣的朋友可以了解下

从本文开始,介绍一下最常用的一个集合对象HashMap,HashMap存储的是键值对,本文采用的基于JDK11的源码实现。 一般大家都知道HashMap是通过put操作把一组键值对(key和value)存储到HashMap中,然后可以通过get(key)去获取key对应的value。而最重要的这两个过程是怎么实现的呢?下面我们就来对put和get这两个过程做一个分析。

HashMap基本工作原理

下面先看一段源码:

/**
   * The table, initialized on first use, and resized as
   * necessary. When allocated, length is always a power of two.
   * (We also tolerate length zero in some operations to allow
   * bootstrapping mechanics that are currently not needed.)
 */
transient Node[] table;

当用户调用put方法的时候把key和value放入到HashMap的时候,这个数组table就是实际存储key和value的地方。HashMap把用户传入的key和value封装成一个Node对象,把该Node对象放入到table对应的位置。Map执行get操作的时候,并没有传入具体的数组的索引位置信息,只是传入了key,因此这个地方就会涉及到一个key转索引的一个操作,然后根据索引获取table中对应位置的Node对象,把value值返回给用户。由于数组的访问时间复杂度是O(1),因此Map的get操作也可以认为是O(1)( 这个地方先暂时理解为O(1),具体原因见后面)。

简单来说,在执行put方法的时候,Map会根据传入的key获取它hashcode值,然后根据hashcode与table大小进行求模运算,得到的值就是它在table数组索引位置。实际这个过程又有点复杂,具体下面开始分析。

HashMap 数组寻址与hash值计算

用户通过key访问map获取value的时候,原理是用key的hash值来与数组的大小取模获取数组的索引。但实际在HashMap实现中,对取模运算进行了一下优化,采用了(n-1) & hash(key)的方法获取数组索引,这里的n是table的大小,hash(key)表示key的哈希值,这种方法可以得到与取模运算一样的效果,但是速度要比取模运算快。

下面看一下,hash(key)的实现逻辑

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

从上面的源码看:

  • 调用key的hashCode()方法获取hashCode值h
  • 把h进行无符号右移16位
  • 把h与h右移后的值进行异或操作最后得到key的hash值。

这里大家比较好奇,为什么会进行这种复杂操作,他的用意是什么?下面来给大家说一下这个过程。

假设 table的大小是16,key1和Key2调用hashCode方法获取的值的二进制形式分别是:

1111 1111 1111 1101 0000 0000 0000 0001  # key1
1111 1111 1111 1111 0000 0000 0000 0001  # key2

首先我们直接使用key1和key2的hashCode获取的值去计算在的table的索引值。
具体过程是:

# key1在table中索引的计算过程与结果
1111 1111 1111 1101 0000 0000 0000 0001 
0000 0000 0000 0000 0000 0000 0000 1111  &  #n-1的二进制
---------------------------------------
0000 0000 0000 0000 0000 0000 0000 0001 # 得到的table索引是1


# key2在table中索引的计算过程与结果
1111 1111 1111 1111 0000 0000 0000 0001 
0000 0000 0000 0000 0000 0000 0000 1111  &  #n-1的二进制
---------------------------------------
0000 0000 0000 0000 0000 0000 0000 0001 #得到的table索引是1

根据上面计算结果可知,虽然key1和key2值不同,但是最后得到的table的索引都是1,这样就会出现了冲突。主要原因是在与n-1进行&操作的时候,通常n的值比较小,因此高16位都是0,这样0和任何数&结果都是0。通常key的hashCode取值很不固定。从最高位到最低位都会出现1的可能。比如key1和key2,他们的区别恰恰是出现在自己的hashCode的高16位,因此key1和key2与n-1进行&操作的结果是一样的。如果key1和key2经过hash()方法处理后呢,来看看结果:

# key1在table中索引的计算过程与结果
  1111 1111 1111 1101 0000 0000 0000 0001 #key1本身
^  0000 0000 0000 0000 1111 1111 1111 1101 #key1右移16的值
-----------------------------------------------
  1111 1111 1111 1111 1111 1111 1111 1100   # hash(key1)计算后的值
&  0000 0000 0000 0000 0000 0000 0000 1111   #n-1的二进制
-----------------------------------------------
  0000 0000 0000 0000 0000 0000 0000 1100 #得到的table索引是12



# key2在table中索引的计算过程与结果
  1111 1111 1111 1111 0000 0000 0000 0001  #key2本身
^  0000 0000 0000 0000 1111 1111 1111 1111  #key2右移16的值
-----------------------------------------------
  1111 1111 1111 1111 1111 1111 1111 1110   #hash(key1)计算后的值
&  0000 0000 0000 0000 0000 0000 0000 1111   #n-1的二进制
-----------------------------------------------
  0000 0000 0000 0000 0000 0000 0000 1110 #得到的table索引是14

这样key1和key2不会出现位置冲突。当key和自己的高16位进行异或操作的后的值的低16位中同时保留了原始key低16位和高16位的特征。因此key1和key2再和n-1进行&运算时,减少了出现相同值的可能性。明白了这些内容内容,下一篇文章开始结束HashMap的put和get方法的实现原理。

以上就是Java HashMap实现原理分析(一)的详细内容,更多关于Java HashMap原理的资料请关注其它相关文章!


推荐阅读
  • window下kafka的安装以及测试
    目录一、安装JDK(需要安装依赖javaJDK)二、安装Kafka三、测试参考在Windows系统上安装消息队列kafka一、安装JDKÿ ... [详细]
  • Kafka Topic 数据管理与清理策略
    本文探讨了在生产环境中如何有效管理和定期清理Kafka Topic中的数据。介绍了基于时间、日志大小和日志起始偏移量三种清除方式,并重点讲解了基于时间的清除策略及其配置方法。 ... [详细]
  • 使用Bootstrap创建响应式渐变固定头部导航栏的方法
    本文详细介绍了如何利用Bootstrap框架构建一个具有渐变效果的固定顶部响应式导航栏,包括HTML结构、CSS样式以及JavaScript交互的完整实现过程。适合前端开发者和学习者参考。 ... [详细]
  • 深入理解Java类加载机制
    本文详细探讨了Java虚拟机(JVM)中类加载器的工作原理,特别是如何通过类的全限定名从外部源获取二进制字节流,以及不同类型的类加载器及其在双亲委派模型中的角色。 ... [详细]
  • 深入解析JVM:类加载子系统详解
    本文旨在深入探讨Java虚拟机(JVM)中的类加载子系统,包括其基本结构、类加载器的工作原理、类的加载过程以及双亲委派机制。通过对这些关键点的详细分析,帮助读者更好地理解和掌握JVM的核心机制。 ... [详细]
  • 本文介绍了如何在Angular CLI创建的项目中安装并配置Bootstrap,包括必要的依赖项jQuery和Popper.js的安装步骤。 ... [详细]
  • PHP 中服务器变量的配置指南
    本文详细介绍了在 PHP 环境下如何正确设置服务器变量的方法,包括变量类型的动态转换及其应用场景。适合初学者及进阶开发者阅读。 ... [详细]
  • Bootstrap与Layui的主要差异分析
    在前端开发领域,Bootstrap与Layui是两种非常流行的框架选择。本文将深入探讨这两种框架的主要区别,帮助开发者根据项目需求做出最佳选择。 ... [详细]
  • 构建Filebeat-Kafka-Logstash-ElasticSearch-Kibana日志收集体系
    本文介绍了如何使用Filebeat、Kafka、Logstash、ElasticSearch和Kibana构建一个高效、可扩展的日志收集与分析系统。各组件分别承担不同的职责,确保日志数据能够被有效收集、处理、存储及可视化。 ... [详细]
  • Netty基础教程:构建简易Netty客户端与服务器
    Java NIO是解决传统阻塞I/O问题的关键技术之一,但其复杂性给开发者带来了挑战。Netty作为一个成熟的网络编程框架,极大地简化了这一过程。本文将通过一个简单的示例,介绍如何使用Netty创建基本的客户端和服务器。 ... [详细]
  • 本文探讨了Jeddict工具的应用价值,特别是在快速构建和部署CRUD服务系统方面的能力。通过介绍其核心功能和优势,以及当前的使用状况,文章还展望了Jeddict未来的改进方向。 ... [详细]
  • 探讨中国IT行业的现状与挑战,分析不同阶段程序员的职业路径选择,特别是对于拥有多年经验的大龄程序员来说,继续深耕技术领域或转向管理层的利弊。 ... [详细]
  • Eureka 注册中心实现密码认证
    自 Spring Cloud 1.1 版本起,Eureka 注册中心支持通过配置文件(如 bootstrap.yml)启用密码验证功能,以增强服务发现的安全性。 ... [详细]
  • 日期:2013年3月19日 来源:GBin1.com 对于希望启动并运行首个网站的新手而言,选择一个合适的CMS或免费平台是至关重要的第一步。本文将为您介绍一系列关于WordPress的设计开发资源和手册,帮助您迅速掌握网站构建技巧。 ... [详细]
  • 探讨 Bootstrap 中是否存在与 jQuery EasyUI 类似的功能丰富的标签页组件,支持通过 URL 加载内容及标签页的关闭功能。 ... [详细]
author-avatar
大笨猫的男人
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有