1.idea debug进入HashMap源码时传参不正确?
2.HashMap 的修改修改初始值和最大值和扩容因子
3.结合源码探究HashMap初始化容量问题
4.HashMap实现原理一步一步分析(1-put方法源码整体过程)
idea debug进入HashMap源码时传参不正确?
我测试了下面的代码:分别在这四个位置打了断点以监控程序的运行情况,debug后,修改修改进入第一次断点的修改修改位置为:
与题主说的情况一致,而没有进入我的修改修改第一个断点进行输出,而后F9:
发现还是修改修改在put文件,经多次F9之后,修改修改facebook跳转好友源码可以看出来,修改修改其实java的修改修改jvm在启动的时候,在底层也自行调用的修改修改put方法,将jvm所需要的修改修改一些动态库、jar包put到某个map之中,修改修改具体是修改修改哪个map看不出来。要等到jvm底层将所有东西准备好后,修改修改才进行main函数。修改修改
jvm准备需要put多少次我就不数了,修改修改现在我先把put的断点取消,让程序debug到我的pcma源码第一个断点处:
这个时候将put方法打上断点,F9发现:
奇怪的key值增加了,它将我的classes编译目录丢进去了,继续F9,和上一步差不多,再再次F9,终于来了:
继续F9,终于到达了我的第二个断点:
继续F9,这次没有put奇怪的东西了:
继续:
最后:
然后程序退出:
综上,jvm在启动的时候会在程序背后隐式地将一些配置啊什么的通过put方法放到某些地方,不用关心,你遇到的情况是正常的也是正确的
HashMap 的初始值和最大值和扩容因子
HashMap 初始化默认值为。你可以通过构造函数自定义初始值。
最大值为1<<,这个值表示2的次方。在HashMap的源码注释中有明确说明。
理解左移操作符<<是cced源码关键,它执行二进制左移操作。例如,1 << x 等同于2的x次方。
当存储元素超过最大值时,HashMap会强制将数组大小capacity设置为最大值。
初始化和扩容时,数组大小capacity被限制在两个地方:通过tableSizeFor()函数设置为2的幂次,不超过最大值;或在容量翻倍时,设置为1 << ,但实际容量为Integer.MAX_VALUE避免整型溢出。
加载因子,即扩容因子,决定何时进行扩容。比如,加载因子为0.5,初始化容量为时,parse源码当元素数达到8个,HashMap会进行扩容。加载因子为0.时,考虑性能与容量平衡。
以上参数在JDK源代码中定义,是使用HashMap的基础。
结合源码探究HashMap初始化容量问题
探究HashMap初始化容量问题
在深入研究HashMap源码时,有一个问题引人深思:为何在知道需要存储n个键值对时,我们通常会选择初始化容量为capacity = n / 0. + 1?
本文旨在解答这一疑惑,适合具备一定HashMap基础知识的读者。请在阅读前,思考以下问题:
让我们通过解答这些问题,逐步展开对HashMap初始化容量的深入探讨。
源码探究
让我们从实际代码出发,通过debug逐步解析HashMap的初始化逻辑。
举例:初始化一个容量为9的全球源码HashMap。
执行代码后,我们发现初始化容量为,且阈值threshold设置为。
解析
通过debug,我们首先关注到构造方法中的初始化逻辑。注意到,初始化阈值时,实际调用的是`tabliSizeFor(int n)`方法,它返回第一个大于等于n的2的幂。例如,`tabliSizeFor(9)`返回,`tabliSizeFor()`返回,`tabliSizeFor(8)`返回8。
继续解析
在构造方法结束后,我们通过debug继续追踪至`put`方法,直至`putVal`方法。
在`putVal`方法中,我们发现当第一次调用`put`时,table为null,从而触发初始化逻辑。在初始化过程中,关键在于`resize()`方法中对新容量`newCap`的初始化,即等于构造方法中设置的阈值`threshold`()。
阈值更新
在初始化后,我们进一步关注`updateNewThr`的代码逻辑,发现新的阈值被更新为新容量乘以负载因子,即 * 0.。
案例分析
举例:初始化一个容量为8的HashMap。
解答:答案是8,因为`tableSizeFor`方法返回大于等于参数的2的幂,而非严格大于。
扩容问题
举例:当初始化容量为时,放入9个不同的entry是否会引发扩容。
解答:不会,因为扩容条件与阈值有关,当map中存储的键值对数量大于阈值时才触发扩容。根据第一问,初始化容量是,阈值为 * 0. = 9,我们只放了9个,因此不会引起扩容。
容量选择
举例:已知需要存储个键值对,如何选择合适的初始化容量。
解答:初始化容量的目的是减少扩容次数以提高效率并节省空间。选择容量时,应考虑既能防止频繁扩容又能充分利用空间。具体选择取决于实际需求和预期键值对的数量。
总结
通过本文的探讨,我们深入了解了HashMap初始化容量背后的逻辑和原因。希望这些解析能够帮助您更深入地理解HashMap的内部工作原理。如果您对此有任何疑问或不同的见解,欢迎在评论区讨论。
最后,如有帮助,欢迎点赞分享。
HashMap实现原理一步一步分析(1-put方法源码整体过程)
本文分享了HashMap内部的实现原理,重点解析了哈希(hash)、散列表(hash table)、哈希码(hashcode)以及hashCode()方法等基本概念。
哈希(hash)是将任意长度的输入通过散列算法转换为固定长度输出的过程,建立一一对应关系。常见算法包括MD5加密和ASCII码表。
散列表(hash table)是一种数据结构,通过关键码值映射到表中特定位置进行快速访问。
哈希码(hashcode)是散列表中对象的存储位置标识,用于查找效率。
Object类中的hashCode()方法用于获取对象的哈希码值,以在散列存储结构中确定对象存储地址。
在存储字母时,使用哈希码值对数组大小取模以适应存储范围,防止哈希碰撞。
HashMap在JDK1.7中使用数组+链表结构,而JDK1.8引入了红黑树以优化性能。
HashMap内部数据结构包含数组和Entry对象,数组用于存储Entry对象,Entry对象用于存储键值对。
在put方法中,首先判断数组是否为空并初始化,然后计算键的哈希码值对数组长度取模,用于定位存储位置。如果发生哈希碰撞,使用链表解决。
本文详细介绍了HashMap的存储机制,包括数组+链表的实现方式,以及如何处理哈希碰撞。后续文章将继续深入探讨HashMap的其他特性,如数组长度的优化、多线程环境下的性能优化和红黑树的引入。