热门标签 | 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;越需要宁静的思考。


推荐阅读
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • 本文介绍了在iOS开发中使用UITextField实现字符限制的方法,包括利用代理方法和使用BNTextField-Limit库的实现策略。通过这些方法,开发者可以方便地限制UITextField的字符个数和输入规则。 ... [详细]
  • 欢乐的票圈重构之旅——RecyclerView的头尾布局增加
    项目重构的Git地址:https:github.comrazerdpFriendCircletreemain-dev项目同步更新的文集:http:www.jianshu.comno ... [详细]
  • 本文整理了Java面试中常见的问题及相关概念的解析,包括HashMap中为什么重写equals还要重写hashcode、map的分类和常见情况、final关键字的用法、Synchronized和lock的区别、volatile的介绍、Syncronized锁的作用、构造函数和构造函数重载的概念、方法覆盖和方法重载的区别、反射获取和设置对象私有字段的值的方法、通过反射创建对象的方式以及内部类的详解。 ... [详细]
  • GetWindowLong函数
    今天在看一个代码里头写了GetWindowLong(hwnd,0),我当时就有点费解,靠,上网搜索函数原型说明,死活找不到第 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • 本文介绍了在处理不规则数据时如何使用Python自动提取文本中的时间日期,包括使用dateutil.parser模块统一日期字符串格式和使用datefinder模块提取日期。同时,还介绍了一段使用正则表达式的代码,可以支持中文日期和一些特殊的时间识别,例如'2012年12月12日'、'3小时前'、'在2012/12/13哈哈'等。 ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • 重入锁(ReentrantLock)学习及实现原理
    本文介绍了重入锁(ReentrantLock)的学习及实现原理。在学习synchronized的基础上,重入锁提供了更多的灵活性和功能。文章详细介绍了重入锁的特性、使用方法和实现原理,并提供了类图和测试代码供读者参考。重入锁支持重入和公平与非公平两种实现方式,通过对比和分析,读者可以更好地理解和应用重入锁。 ... [详细]
  • 本文介绍了RxJava在Android开发中的广泛应用以及其在事件总线(Event Bus)实现中的使用方法。RxJava是一种基于观察者模式的异步java库,可以提高开发效率、降低维护成本。通过RxJava,开发者可以实现事件的异步处理和链式操作。对于已经具备RxJava基础的开发者来说,本文将详细介绍如何利用RxJava实现事件总线,并提供了使用建议。 ... [详细]
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社区 版权所有