【HashMap】链表和红黑树互相转换的几种情况和数组的扩容机制

在Java的HashMap实现中,链表转换为红黑树的条件包括链表长度和HashMap的容量(桶数组大小)。具体规则如下:

  1. 链表长度阈值:当单个桶中的链表长度达到8时,该链表会被转换为红黑树。
  2. 最小树化容量HashMap的总容量(桶数组大小)必须至少为64。如果容量小于64,即使链表长度达到8,也不会进行树化,而是会选择扩容。

基于这些规则,分别研究转换情况:

一、链表长度超过 8,数组大小小于 64

  • 如果链表长度超过了长度阈值 8。
  • 如果数组大小等于16,小于最小树化容量64。

在这种情况下,即使链表长度超过了8,由于总容量小于64,链表不会转为红黑树,而是会进行扩容操作。

代码示例

下面是一个示例代码,展示当链表长度等于9且数组大小等于16时,HashMap会如何处理:

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;public class HashMapTreeifyExample {public static void main(String[] args) throws Exception {// 初始化容量为16的HashMapMap<Integer, String> map = new HashMap<>(16);// 插入元素,确保某个桶中的链表长度达到9for (int i = 0; i < 9; i++) {map.put(i, "value" + i);}// 通过反射获取HashMap的内部结构Class<?> mapType = map.getClass();Field tableField = mapType.getDeclaredField("table");tableField.setAccessible(true);Object[] table = (Object[]) tableField.get(map);// 输出当前的容量System.out.println("当前的容量: " + table.length);// 检查每个桶的元素数量和类型for (Object node : table) {if (node != null) {Class<?> nodeType = node.getClass();if (nodeType.getSimpleName().equals("TreeNode")) {System.out.println("链表已经转换为红黑树。");} else {System.out.println("仍然是链表节点。");}}}}
}

代码解释

  1. 初始化容量为16的HashMap

    • 我们创建了一个初始容量为16的HashMap
  2. 插入元素

    • 插入9个元素,确保某个桶中的链表长度达到9。
  3. 通过反射获取HashMap的内部结构

    • 使用反射技术访问HashMap的内部数组(桶数组)。
  4. 检查桶中的节点类型

    • 遍历桶,检查每个桶中的节点是链表节点还是红黑树节点,并输出当前容量。

预期结果

  • 因为链表长度为9,超过了8,但由于容量(16)小于64,HashMap不会将链表转换为红黑树。
  • 反而,HashMap会选择扩容,将容量增加到32,然后重新分配所有元素。

结论

在此示例中,由于链表长度为9超过了8,但HashMap的容量只有16,小于64,因此不会进行树化操作,而是会选择扩容。扩容后,所有元素会被重新分配到新的桶数组中。

二、链表长度没有超过 8,数组大小大于 64

  • 如果链表长度等于 5,没有超过长度阈值 8。
  • 如果数组大小等于80,大于最小书画容量 64。

链表会在其长度达到 8 时才会转换为红黑树。因此,如果链表长度等于 5,那么它不会转为红黑树,无论数组(即HashMap的桶数组)大小是多少。

代码示例

下面的代码展示了HashMap在链表长度为5时的行为:

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;public class HashMapTreeifyExample {public static void main(String[] args) throws Exception {Map<Integer, String> map = new HashMap<>(80);  // 初始化容量为80// 插入导致单个桶中链表长度达到5的键值对for (int i = 0; i < 5; i++) {map.put(i, "value" + i);}// 通过反射获取HashMap的内部结构Class<?> mapType = map.getClass();Field tableField = mapType.getDeclaredField("table");tableField.setAccessible(true);Object[] table = (Object[]) tableField.get(map);// 遍历桶,检查是否有红黑树节点for (Object node : table) {if (node != null) {Class<?> nodeType = node.getClass();if (nodeType.getSimpleName().equals("TreeNode")) {System.out.println("链表已经转换为红黑树。");} else {System.out.println("仍然是链表节点。");}}}}
}

代码解释

  1. 初始化容量为80的HashMap

    • 我们创建了一个初始容量为80的HashMap
  2. 插入元素

    • 插入5个元素,确保某个桶中的链表长度达到5。
  3. 通过反射获取HashMap的内部结构

    • 使用反射技术访问HashMap的内部数组(桶数组)。
  4. 检查桶中的节点类型

    • 遍历桶,检查每个桶中的节点是链表节点还是红黑树节点,并输出当前容量。

预期结果

  • 链表长度为 5 时,所有节点仍然是链表节点,不会转换为红黑树。

结论

只有当单个桶中的链表长度达到8并且HashMap的容量大于等于64时,链表才会转换为红黑树。链表长度为5时,不会进行转换。

三、红黑树转换为链表

HashMap实现中,红黑树会在一定条件下转换回链表。这主要是为了在删除元素后,保持合适的数据结构以优化性能和空间使用。红黑树转换为链表的条件如下:

  1. 树形化的红黑树节点数量小于6:当红黑树节点的数量减少到6或更少时,红黑树会被转换回链表。这是因为在少量节点的情况下,链表的插入和删除操作比红黑树更高效。

  2. 最小树化容量:这是一个辅助条件,用于确保只有在HashMap的容量(桶数组大小)足够大时,才会执行链表到红黑树的转换和反转换。默认情况下,这个值是64。但是,转回链表的主要依据还是节点数量。

代码示例

下面是一个示例代码,展示红黑树在元素删除后如何转换回链表:

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;public class HashMapDemoteTreeExample {public static void main(String[] args) throws Exception {// 初始化容量为64的HashMapMap<Integer, String> map = new HashMap<>(64);// 插入足够多的元素,确保某个桶中的链表长度达到8,转化为红黑树for (int i = 0; i < 8; i++) {map.put(i, "value" + i);}// 通过反射获取HashMap的内部结构Class<?> mapType = map.getClass();Field tableField = mapType.getDeclaredField("table");tableField.setAccessible(true);Object[] table = (Object[]) tableField.get(map);// 检查红黑树节点System.out.println("插入元素后,检查节点类型:");checkAndPrintNodeType(table);// 删除部分元素,触发从红黑树到链表的转换map.remove(7);map.remove(6);map.remove(5);// 重新获取HashMap的内部结构table = (Object[]) tableField.get(map);// 检查是否已经从红黑树转回链表System.out.println("删除元素后,检查节点类型:");checkAndPrintNodeType(table);}private static void checkAndPrintNodeType(Object[] table) throws Exception {for (Object node : table) {if (node != null) {Class<?> nodeType = node.getClass();if (nodeType.getSimpleName().equals("TreeNode")) {System.out.println("红黑树节点。");} else {System.out.println("链表节点。");}}}}
}

代码解释

  1. 初始化容量为64的HashMap

    • 创建一个初始容量为64的HashMap,以确保足够的容量进行树化操作。
  2. 插入元素

    • 插入8个元素,确保某个桶中的链表长度达到8,触发从链表到红黑树的转换。
  3. 通过反射获取HashMap的内部结构

    • 使用反射技术访问HashMap的内部数组(桶数组)。
  4. 检查节点类型

    • 在插入元素后,检查桶中的节点类型,验证红黑树节点的存在。
  5. 删除部分元素

    • 删除足够的元素,减少红黑树的节点数量到6以下,触发从红黑树到链表的转换。
  6. 再次检查节点类型

    • 在删除元素后,重新检查桶中的节点类型,验证红黑树是否转换回链表。

预期结果

  • 在插入8个元素后,某些桶中的节点应该会从链表转换为红黑树节点。
  • 在删除元素后,某些红黑树节点会转换回链表节点。

结论

HashMap的红黑树节点数量减少到6或以下时,红黑树会被转换回链表。这种转换是为了在节点数量较少时,优化数据结构的性能和空间使用。

四、数组的扩容

HashMap的扩容是其内部机制之一,用于确保在存储大量元素时性能不会显著下降。HashMap通过调整内部数组(桶数组)的大小来管理其负载因子和冲突的数量。

扩容机制

  1. 触发条件

    • HashMap在插入元素时,如果元素数量超过了当前容量乘以负载因子(默认值是0.75),就会触发扩容操作。
    • 例如,默认情况下,当HashMap的容量为16时,如果元素数量超过16 * 0.75 = 12,就会触发扩容。
  2. 扩容过程

    • 扩容时,HashMap的容量会变为原来的两倍。
    • 创建一个新的桶数组,新的容量是旧容量的两倍。
    • 重新散列(rehash)所有现有的键值对,将它们放入新的桶中。
  3. 性能影响

    • 扩容是一个相对昂贵的操作,因为它需要重新计算所有键的哈希值并将它们重新插入到新的桶数组中。
    • 因此,频繁的扩容会影响性能,选择合适的初始容量可以减少扩容次数。

代码示例

以下是一个示例代码,展示了HashMap在插入元素时如何扩容:

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;public class HashMapResizeExample {public static void main(String[] args) throws Exception {// 初始化容量为16的HashMapMap<Integer, String> map = new HashMap<>(16);// 插入13个元素,触发扩容for (int i = 0; i < 13; i++) {map.put(i, "value" + i);}// 通过反射获取HashMap的内部结构Class<?> mapType = map.getClass();Field tableField = mapType.getDeclaredField("table");tableField.setAccessible(true);Object[] table = (Object[]) tableField.get(map);// 输出扩容前后的容量System.out.println("扩容后的容量: " + table.length);  // 应该是32// 检查每个桶的元素数量for (int i = 0; i < table.length; i++) {if (table[i] != null) {System.out.print("桶 " + i + " 中的元素: ");printBucket(table[i]);System.out.println();}}}// 打印桶中的元素private static void printBucket(Object node) throws Exception {Class<?> nodeClass = node.getClass();Field keyField = nodeClass.getDeclaredField("key");Field valueField = nodeClass.getDeclaredField("value");Field nextField = nodeClass.getDeclaredField("next");keyField.setAccessible(true);valueField.setAccessible(true);nextField.setAccessible(true);while (node != null) {Object key = keyField.get(node);Object value = valueField.get(node);System.out.print("[" + key + "=" + value + "] ");node = nextField.get(node);}}
}

详细解释

  1. 初始化

    • 我们初始化一个HashMap,初始容量为16。
  2. 插入元素

    • 插入13个元素,这会超过默认的负载因子(0.75)所允许的12个元素,从而触发扩容。
  3. 扩容后的检查

    • 使用反射获取HashMap的内部结构,特别是桶数组。
    • 输出扩容后的容量,应该是32,因为容量会翻倍。
    • 打印每个桶中的元素,验证重新散列的正确性。

可以看到HashMap的扩容机制如何在插入大量元素时调整内部结构,以确保高效的性能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/848558.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第二证券:A股重磅调整!富时中国A50指数将纳入中远海控中国中车

重要的音讯有哪些 A股&#xff0c;重磅调整&#xff01; 6月5日&#xff0c;富时罗素宣布对富时我国50指数、富时我国A50指数、富时我国A150指数、富时我国A200指数、富时我国A400指数的季度审阅变更。该变更将于2024年6月21日星期五收盘后&#xff08;即2024年6月24日星期一…

055、Python 关于全局变量和局部变量

定义&#xff1a; 全局变量&#xff1a;在函数外部定义的变量&#xff0c;可以在整个程序中访问。 局部变量&#xff1a;在函数内部定义的变量&#xff0c;只能在其定义的函数内部访问。 作用域优先级&#xff1a; 在 Python 中&#xff0c;作用域的查找顺序是&#xff1a;…

【Linux之·readelf工具·二进制程序处理工具】

系列文章目录 文章目录 前言一、使用readelf工具查看程序代码变量的内存空间布局情况1.1 源程序与程序的映射1.2 程序到进程的映射 二、readelf指令2.1 节头信息2.2符号表段中的项 总结 前言 在现代软件开发中&#xff0c;了解和理解可执行文件和共享库的结构变得越来越重要。而…

免费,C++蓝桥杯比赛历年真题--第14届蓝桥杯省赛真题(含答案解析和代码)

C蓝桥杯比赛历年真题–第14届蓝桥杯省赛真题 一、选择题 答案&#xff1a;A 解析&#xff1a; C中 bool 类型与 char 类型一样&#xff0c;都需要1 byte。一些其他类型的占用字节数:short:2 byte&#xff0c;int:4byte&#xff0c;long long:8 byte&#xff0c;double:8byte&…

探究MySQL中的“树”结构

1 引言 树高千丈,叶落求索 – 唐代杜牧 树结构在MySQL中常用于表示层次关系,如组织结构或分类体系。引入树结构可使数据之间建立父子关系,便于查询和管理。益处包括快速检索子节点、方便展示层次关系、支持递归查询等。 2 基础概念 2.1 名词解析 程序就像是一张有向图,你…

Linux操作系统:Spark在虚拟环境下的安装及部署

将Spark安装到指定目录 // 通过wget下载Spark安装包 $ wget https://d3kbcqa49mib13.cloudfront.net/spark-2.1.1-bin-hadoop2.7.tgz // 将spark解压到安装目录 $ tar –zxvf spark-2.1.1-bin-hadoop2.7.tgz –C /usr/local/ // 重命名 $ mv /usr/local/spark-2.1.1-bin-hado…

面试 Redis 八股文十问十答第三期

面试 Redis 八股文十问十答第三期 作者&#xff1a;程序员小白条&#xff0c;个人博客 相信看了本文后&#xff0c;对你的面试是有一定帮助的&#xff01;关注专栏后就能收到持续更新&#xff01; ⭐点赞⭐收藏⭐不迷路&#xff01;⭐ 1&#xff09;redis 的 lua 脚本用过吗&…

前端怎么debugger排查线上问题

前端怎么debugger排查线上问题 1.问题背景2.问题详细说明3.处理方案a.开发环境怎么找&#xff0c;步骤一样的&#xff1a;b.生产环境怎么找&#xff0c;步骤一样的&#xff1a;还有一种情况就是你的子盒子是使用csshover父盒子出来的&#xff0c; 4.demo地址&#xff1a; 1.问题…

OOP 下一个排列(函数模板)

题目描述 输入一个序列&#xff0c;输出其下一个字典序的排列 如&#xff1a;输入1 2 3&#xff0c; 下一个为1 3 2&#xff0c; 下一个为2 1 3&#xff0c; 下一个为2 3 1&#xff0c; 下一个为3 1 2&#xff0c; 下一个为3 2 1&#xff08;最大的字典序排列&#xff0…

C++基础-编程练习题和答案(数组2)

文章目录 前言一、植树二、校门外的树三、排除第一个异形基因四、比身高五、supercell做核酸 前言 在C中&#xff0c;数组是一种数据结构&#xff0c;它允许在内存中连续存储相同类型的元素。数组是静态的&#xff0c;这意味着它们在编译时必须指定大小&#xff0c;并且在程序…

java版MES系统全套源码,支持 SaaS 多租户,管理后台的 Vue3 版本采用 :vue-element-plus-admin

MES生产制造执行系统源码&#xff0c;有演示&#xff0c;自主研发&#xff0c;多个项目应用案例&#xff0c;成熟稳定。支持二次开发&#xff0c;商业授权后可商用。 MES系统是面向制造企业车间执行层的生产信息化管理系统&#xff0c;能实时监控生产过程、管理制造数据、优化生…

惊呆了!六西格玛培训竟然这么强大!——张驰咨询

六西格玛&#xff0c;这个在业界久负盛名的管理理念&#xff0c;它的魅力太强大了。曾听闻它能帮助企业和个人提升竞争力&#xff0c;但当真正走进这个培训体系时&#xff0c;会发现它的影响力远超你的想象。 在六西格玛的指导下&#xff0c;企业实现了显著的转变。之前那些看…

vue使用html2canvas截图下载时,存在svg或者img或者特殊字体时截图不全的解决办法

使用html2canvas进行div截图时&#xff0c;存在svg和img的解决办法 写在前面&#xff1a;vue使用html2canvas截图时&#xff0c;存在svg或者img或者特殊字体时截图时空白&#xff0c;或者不全解决办法如下第一步&#xff0c;svg或者img先转base64&#xff08;如果是特殊字体&am…

优化家庭网络,路由器无线中继配置全攻略(中兴E1600无线中继设置/如何解决没有预埋有线网络接口的问题/使用闲置路由实现WIFI扩展)

文章目录 📖 介绍 📖🏡 演示环境 🏡📒 网络优化 📒📒 操作步骤 📒💡适用场景🚨 常见问题及解决方案⚓️ 相关链接 ⚓️📖 介绍 📖 在现代家庭生活中,WiFi已经渗透到我们生活的每一个角落,成为了日常生活中不可或缺的一部分。然而,不少用户常常遇到W…

区块链简要介绍及运用的技术

一、区块链的由来 区块链概念最早是从比特币衍生出来的。 比特币&#xff08;Bitcoin&#xff09;诞生于2008年&#xff0c;是由一个名叫中本聪&#xff08;Satoshi Nakamoto&#xff09;的人首次提出&#xff0c;这个人非常神秘&#xff0c;至今没有他的任何准确信息。在提出…

C++ Primer Chapter 4 Expressions

Chapter 4 Expressions 4.11 类型转换 4.11.2 其他隐式类型转换 数组转换成指针&#xff1a; 在大多数用到数组的表达式中&#xff0c;数组自动转换成指向数组首元素的指针&#xff1a; int ia[10]; int* ipa;♜ 当数组被用作decltype关键字的参数&#xff0c;或者作为取地…

每天一个数据分析题(三百五十三)序列类图表

比较类图表主要用于在实际值与目标值之间、不同对象之间或者不同区域之间进行数值结果的对比分析&#xff0c;下列属于比较类图表的是&#xff1f; A. 油量表 B. 词云图 C. 染色地图 D. 面积图 数据分析认证考试介绍&#xff1a;点击进入 题目来源于CDA模拟题库 点击此处…

Windows下Qt5.14.2连接华为IoTDA平台

一、华为IoTDA简介 华为云物联网平台&#xff08;IoT 设备接入云服务&#xff09;提供海量设备的接入和管理能力&#xff0c;将物理设备联接到云&#xff0c;支撑设备数据采集上云和云端下发命令给设备进行远程控制&#xff0c;配合华为云其他产品&#xff0c;帮助您快速构筑物…

教师自费出书的注意事项有哪些?

备案主编专著的优势&#xff1a;&#xff08;qkfb88688&#xff09; 1、副高、正高职称最高学术成果 2、专著可以代替核心 3、周期短、出书快、可重复使用 4、双号齐全&#xff1a;ISBN&#xff5e;CIP 5、版权长期有效 教师自费出书有以下一些注意事项&#xff1a; 关于书稿&…

在CentOS 7上查看和管理内存使用情况

在Linux系统中&#xff0c;内存管理是一个至关重要的方面&#xff0c;尤其在生产环境中&#xff0c;了解系统内存的使用情况可以帮助管理员优化系统性能&#xff0c;检测内存泄漏&#xff0c;合理分配资源&#xff0c;从而确保系统的稳定运行。本文将详细介绍在CentOS 7系统中如…