热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

[Java8HashMap详解系列]2.HashMap中Key的index是怎样计算的?

[Java8HashMap详解系列]文章目录1.HashMap的存储数据结构2.HashMap中Key的index是怎样计算的?3.HashMap的put()方法执行原理4
[Java 8 HashMap 详解系列] 文章目录

1.HashMap 的存储数据结构


2.HashMap 中 Key 的 index 是怎样计算的?


3.HashMap 的 put() 方法执行原理


4.HashMap 的 get() 方法执行原理


5.HashMap 的 remove() 方法执行原理


6.HashMap 的扩容 resize() 原理


7.HashMap 中的红黑树原理




2.HashMap 中 Key 的 index 是怎样计算的?

HashMap中的 table 是怎样确定数组索引位置的?

对于HashMap内的所有实现来说,首先第一步是定位对键值对所在数组的索引下标位置,这是后续所有操作的基础.

如下代码是展示索引下标获取的基本逻辑:

/* ---------------- Static utilities -------------- *//*** Computes key.hashCode() and spreads (XORs) higher bits of hash* to lower. Because the table uses power-of-two masking, sets of* hashes that vary only in bits above the current mask will* always collide. (Among known examples are sets of Float keys* holding consecutive whole numbers in small tables.) So we* apply a transform that spreads the impact of higher bits* downward. There is a tradeoff between speed, utility, and* quality of bit-spreading. Because many common sets of hashes* are already reasonably distributed (so don't benefit from* spreading), and because we use trees to handle large sets of* collisions in bins, we just XOR some shifted bits in the* cheapest possible way to reduce systematic lossage, as well as* to incorporate impact of the highest bits that would otherwise* never be used in index calculations because of table bounds.*/static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}

然后, 数组元素的下标算法是:

public static final int index(int hash, int n) {return (n - 1) & hash;}

代码拆解:

public static final int hash(Object key) {if (key == null) {return 0;}int h = key.hashCode();int rh = h >>> 16;out.println("===================== hash(" + key + ") ===========================");out.println("key \t\t" + key);out.println("h=key.hashCode() \t\t" + toBinary(h) + "\t\t" + h);out.println("rh=h>>>16 \t\t" + toBinary(rh) + "\t\t" + rh);return h ^ rh;}public static final int index(int hash, int n) {out.println("hash=h ^ rh \t\t" + toBinary(hash) + "\t\t" + hash);out.println("n = \t\t" + toBinary(n) + "\t\t" + (n));out.println("n - 1= \t\t" + toBinary(n - 1) + "\t\t" + (n - 1));int index = (n - 1) & hash;out.println("index=(n - 1) & hash \t\t" + toBinary(index) + "\t\t" + index);out.println();return index;}

运行测试数据:


===================== hash(a) ===========================
key a
h=key.hashCode() 00000000000000000000000001100001 97
rh=h>>>16 00000000000000000000000000000000 0
hash(a)=97
hash=h ^ rh 00000000000000000000000001100001 97
n = 00000000000000000000000000000001 1
n - 1= 00000000000000000000000000000000 0
index=(n - 1) & hash 00000000000000000000000000000000 0===================== hash(b) ===========================
key b
h=key.hashCode() 00000000000000000000000001100010 98
rh=h>>>16 00000000000000000000000000000000 0
hash(b)=98
hash=h ^ rh 00000000000000000000000001100010 98
n = 00000000000000000000000000000010 2
n - 1= 00000000000000000000000000000001 1
index=(n - 1) & hash 00000000000000000000000000000000 0===================== hash(abc) ===========================
key abc
h=key.hashCode() 00000000000000010111100001100010 96354
rh=h>>>16 00000000000000000000000000000001 1
hash(abc)=96355
hash=h ^ rh 00000000000000010111100001100011 96355
n = 00000000000000000000000000000100 4
n - 1= 00000000000000000000000000000011 3
index=(n - 1) & hash 00000000000000000000000000000011 3===================== hash(abcde) ===========================
key abcde
h=key.hashCode() 00000101100001001111010001100011 92599395
rh=h>>>16 00000000000000000000010110000100 1412
hash(abcde)=92598759
hash=h ^ rh 00000101100001001111000111100111 92598759
n = 00000000000000000000000000001000 8
n - 1= 00000000000000000000000000000111 7
index=(n - 1) & hash 00000000000000000000000000000111 7===================== hash(abcdefg) ===========================
key abcdefg
h=key.hashCode() 10111000000110010111010001100100 -1206291356
rh=h>>>16 00000000000000001011100000011001 47129
hash(abcdefg)=-1206268803
hash=h ^ rh 10111000000110011100110001111101 -1206268803
n = 00000000000000000000000000001000 8
n - 1= 00000000000000000000000000000111 7
index=(n - 1) & hash 00000000000000000000000000000101 5

算法图解:

1233356-ae8debd2dad07f5a.png

源码分析:

/*** Returns the value to which the specified key is mapped,* or {@code null} if this map contains no mapping for the key.**

More formally, if this map contains a mapping from a key* {@code k} to a value {@code v} such that {@code (key==null ? k==null :* key.equals(k))}, then this method returns {@code v}; otherwise* it returns {@code null}. (There can be at most one such mapping.)**

A return value of {@code null} does not necessarily* indicate that the map contains no mapping for the key; it's also* possible that the map explicitly maps the key to {@code null}.* The {@link #containsKey containsKey} operation may be used to* distinguish these two cases.** @see #put(Object, Object)*/public V get(Object key) {Node e;return (e = getNode(hash(key), key)) == null ? null : e.value;}/*** Implements Map.get and related methods** @param hash hash for key* @param key the key* @return the node, or null if none*/final Node getNode(int hash, Object key) {Node[] tab; Node first, e; int n; K k;if ((tab = table) != null && (n = tab.length) > 0 &&(first = tab[(n - 1) & hash]) != null) { // index = (n - 1) & hashif (first.hash == hash && // always check first node((k = first.key) == key || (key != null && key.equals(k))))return first;if ((e = first.next) != null) {if (first instanceof TreeNode)return ((TreeNode)first).getTreeNode(hash, key);do {if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))return e;} while ((e = e.next) != null);}}return null;}

HashMap 中的 tableSizeFor() 方法详解

输入: int cap;
返回: n = tableSizeFor(cap)
其中, n % 2 &#61;&#61;0, and cap <&#61;n.

/*** Returns a power of two size for the given target capacity.*/static final int tableSizeFor(int cap) {// 先 cap 减 1int n &#61; cap - 1;n |&#61; n >>> 1;n |&#61; n >>> 2;n |&#61; n >>> 4;n |&#61; n >>> 8;n |&#61; n >>> 16;return (n <0) ? 1 : (n >&#61; MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n &#43; 1; // 再加 1}

我们来运行测试一下:

&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;tableSizeFor(2)&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;
cap &#61; 2
n &#61; cap - 1 00000000000000000000000000000001
n &#61; n | (n >>> 1) 00000000000000000000000000000001
n &#61; n | (n >>> 2) 00000000000000000000000000000001
n &#61; n | (n >>> 4) 00000000000000000000000000000001
n &#61; n | (n >>> 8) 00000000000000000000000000000001
n &#61; n | (n >>> 16) 00000000000000000000000000000001
tableSizeFor &#61; 2
2
&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;tableSizeFor(5)&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;
cap &#61; 5
n &#61; cap - 1 00000000000000000000000000000100
n &#61; n | (n >>> 1) 00000000000000000000000000000110
n &#61; n | (n >>> 2) 00000000000000000000000000000111
n &#61; n | (n >>> 4) 00000000000000000000000000000111
n &#61; n | (n >>> 8) 00000000000000000000000000000111
n &#61; n | (n >>> 16) 00000000000000000000000000000111
tableSizeFor &#61; 8
8
&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;tableSizeFor(10)&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;
cap &#61; 10
n &#61; cap - 1 00000000000000000000000000001001
n &#61; n | (n >>> 1) 00000000000000000000000000001101
n &#61; n | (n >>> 2) 00000000000000000000000000001111
n &#61; n | (n >>> 4) 00000000000000000000000000001111
n &#61; n | (n >>> 8) 00000000000000000000000000001111
n &#61; n | (n >>> 16) 00000000000000000000000000001111
tableSizeFor &#61; 16
16
&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;tableSizeFor(25)&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;
cap &#61; 25
n &#61; cap - 1 00000000000000000000000000011000
n &#61; n | (n >>> 1) 00000000000000000000000000011100
n &#61; n | (n >>> 2) 00000000000000000000000000011111
n &#61; n | (n >>> 4) 00000000000000000000000000011111
n &#61; n | (n >>> 8) 00000000000000000000000000011111
n &#61; n | (n >>> 16) 00000000000000000000000000011111
tableSizeFor &#61; 32
32
&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;tableSizeFor(58)&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;
cap &#61; 58
n &#61; cap - 1 00000000000000000000000000111001
n &#61; n | (n >>> 1) 00000000000000000000000000111101
n &#61; n | (n >>> 2) 00000000000000000000000000111111
n &#61; n | (n >>> 4) 00000000000000000000000000111111
n &#61; n | (n >>> 8) 00000000000000000000000000111111
n &#61; n | (n >>> 16) 00000000000000000000000000111111
tableSizeFor &#61; 64
64
&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;tableSizeFor(100)&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;
cap &#61; 100
n &#61; cap - 1 00000000000000000000000001100011
n &#61; n | (n >>> 1) 00000000000000000000000001110011
n &#61; n | (n >>> 2) 00000000000000000000000001111111
n &#61; n | (n >>> 4) 00000000000000000000000001111111
n &#61; n | (n >>> 8) 00000000000000000000000001111111
n &#61; n | (n >>> 16) 00000000000000000000000001111111
tableSizeFor &#61; 128
128
&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;tableSizeFor(896)&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;
cap &#61; 896
n &#61; cap - 1 00000000000000000000001101111111
n &#61; n | (n >>> 1) 00000000000000000000001111111111
n &#61; n | (n >>> 2) 00000000000000000000001111111111
n &#61; n | (n >>> 4) 00000000000000000000001111111111
n &#61; n | (n >>> 8) 00000000000000000000001111111111
n &#61; n | (n >>> 16) 00000000000000000000001111111111
tableSizeFor &#61; 1024
1024
&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;tableSizeFor(1073741000)&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;
cap &#61; 1073741000
n &#61; cap - 1 00111111111111111111110011000111
n &#61; n | (n >>> 1) 00111111111111111111111011100111
n &#61; n | (n >>> 2) 00111111111111111111111111111111
n &#61; n | (n >>> 4) 00111111111111111111111111111111
n &#61; n | (n >>> 8) 00111111111111111111111111111111
n &#61; n | (n >>> 16) 00111111111111111111111111111111
tableSizeFor &#61; 1073741824
1073741824

参考资料

https://blog.csdn.net/fan2012huan/article/details/51097331
https://www.jianshu.com/p/cbe3f22793be
https://www.jianshu.com/p/dbbecc36200d



Kotlin 开发者社区
1233356-4cc10b922a41aa80

国内第一Kotlin 开发者社区公众号&#xff0c;主要分享、交流 Kotlin 编程语言、Spring Boot、Android、React.js/Node.js、函数式编程、编程思想等相关主题。

越是喧嚣的世界&#xff0c;越需要宁静的思考。


推荐阅读
  • 在处理大图片时,PHP 常常会遇到内存溢出的问题。为了避免这种情况,建议避免使用 `setImageBitmap`、`setImageResource` 或 `BitmapFactory.decodeResource` 等方法直接加载大图。这些函数在处理大图片时会消耗大量内存,导致应用崩溃。推荐采用分块处理、图像压缩和缓存机制等策略,以优化内存使用并提高处理效率。此外,可以考虑使用第三方库如 ImageMagick 或 GD 库来处理大图片,这些库提供了更高效的内存管理和图像处理功能。 ... [详细]
  • 本文总结了Java初学者需要掌握的六大核心知识点,帮助你更好地理解和应用Java编程。无论你是刚刚入门还是希望巩固基础,这些知识点都是必不可少的。 ... [详细]
  • JUC(三):深入解析AQS
    本文详细介绍了Java并发工具包中的核心类AQS(AbstractQueuedSynchronizer),包括其基本概念、数据结构、源码分析及核心方法的实现。 ... [详细]
  • 零拷贝技术是提高I/O性能的重要手段,常用于Java NIO、Netty、Kafka等框架中。本文将详细解析零拷贝技术的原理及其应用。 ... [详细]
  • 在多线程并发环境中,普通变量的操作往往是线程不安全的。本文通过一个简单的例子,展示了如何使用 AtomicInteger 类及其核心的 CAS 无锁算法来保证线程安全。 ... [详细]
  • 本文详细介绍了 PHP 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
  • 本文是Java并发编程系列的开篇之作,将详细解析Java 1.5及以上版本中提供的并发工具。文章假设读者已经具备同步和易失性关键字的基本知识,重点介绍信号量机制的内部工作原理及其在实际开发中的应用。 ... [详细]
  • 使用 ListView 浏览安卓系统中的回收站文件 ... [详细]
  • 优化后的标题:深入探讨网关安全:将微服务升级为OAuth2资源服务器的最佳实践
    本文深入探讨了如何将微服务升级为OAuth2资源服务器,以订单服务为例,详细介绍了在POM文件中添加 `spring-cloud-starter-oauth2` 依赖,并配置Spring Security以实现对微服务的保护。通过这一过程,不仅增强了系统的安全性,还提高了资源访问的可控性和灵活性。文章还讨论了最佳实践,包括如何配置OAuth2客户端和资源服务器,以及如何处理常见的安全问题和错误。 ... [详细]
  • 本文介绍了如何利用ObjectMapper实现JSON与JavaBean之间的高效转换。ObjectMapper是Jackson库的核心组件,能够便捷地将Java对象序列化为JSON格式,并支持从JSON、XML以及文件等多种数据源反序列化为Java对象。此外,还探讨了在实际应用中如何优化转换性能,以提升系统整体效率。 ... [详细]
  • Presto:高效即席查询引擎的深度解析与应用
    本文深入解析了Presto这一高效的即席查询引擎,详细探讨了其架构设计及其优缺点。Presto通过内存到内存的数据处理方式,显著提升了查询性能,相比传统的MapReduce查询,不仅减少了数据传输的延迟,还提高了查询的准确性和效率。然而,Presto在大规模数据处理和容错机制方面仍存在一定的局限性。本文还介绍了Presto在实际应用中的多种场景,展示了其在大数据分析领域的强大潜力。 ... [详细]
  • 本指南从零开始介绍Scala编程语言的基础知识,重点讲解了Scala解释器REPL(读取-求值-打印-循环)的使用方法。REPL是Scala开发中的重要工具,能够帮助初学者快速理解和实践Scala的基本语法和特性。通过详细的示例和练习,读者将能够熟练掌握Scala的基础概念和编程技巧。 ... [详细]
  • Android中将独立SO库封装进JAR包并实现SO库的加载与调用
    在Android开发中,将独立的SO库封装进JAR包并实现其加载与调用是一个常见的需求。本文详细介绍了如何将SO库嵌入到JAR包中,并确保在外部应用调用该JAR包时能够正确加载和使用这些SO库。通过这种方式,开发者可以更方便地管理和分发包含原生代码的库文件,提高开发效率和代码复用性。文章还探讨了常见的问题及其解决方案,帮助开发者避免在实际应用中遇到的坑。 ... [详细]
  • Spring – Bean Life Cycle
    Spring – Bean Life Cycle ... [详细]
  • 深入浅析JVM垃圾回收机制与收集器概述
    本文基于《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)》的阅读心得进行整理,详细探讨了JVM的垃圾回收机制及其各类收集器的特点与应用场景。通过分析不同垃圾收集器的工作原理和性能表现,帮助读者深入了解JVM内存管理的核心技术,为优化Java应用程序提供实用指导。 ... [详细]
author-avatar
sweet梓潼_470
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有