我的编程空间,编程开发者的网络收藏夹
学习永远不晚

hashmap的扩容机制怎么理解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

hashmap的扩容机制怎么理解

今天小编给大家分享一下hashmap的扩容机制怎么理解的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

hashmap的扩容机制是:重新计算容量,用一个新的数组替换原来的数组。重新计算原数组的所有数据并插入一个新数组,然后指向新数组;如果数组在容量扩展前已达到最大值,则直接将阈值设置为最大整数返回。

什么是扩容(resize)?

 扩容(resize):就是重新计算容量,向HashMap对象里不停的添加元素,而HashMap对象内部的数组无法装载更多的元素时,对象就需要扩大数组的长度,以便能装入更多的元素。当然Java里的数组是无法自动扩容的,方法是使用一个新的数组代替已有的容量小的数组,就像我们用一个小桶装水,如果想装更多的水,就得换大水桶。

什么时候扩容?

 当向容器添加元素的时候,会判断当前容器的元素个数,如果大于等于阈值(threshold),即当前容器内的元素个数大于当前数组的长度乘以加载因子的值的时候,就要自动扩容了。

hashmap扩容原理

hashMap扩容就是重新计算容量,向hashMap不停的添加元素,当hashMap无法装载新的元素,对象将需要扩大数组容量,以便装入更多的元素。

hashmap的扩容机制怎么理解

HashMap容量扩展的特性,加载因子越大,空间利用率越高,扩容前需要填充的元素越多,put操作越快,但链表容易过长,hash碰撞概率大,get操作慢。加载因子越小,get操作越快,链表越短,hash碰撞概率低。但是,空间利用率低。put元素太多会导致频繁扩容,影响性能。

hashmap的扩容机制怎么理解

HashMap的容量扩展原理:Hashmap的方法是用新数组替换原数组,重新计算原数组中的所有数据,插入新数组,然后指向新数组;如果数组在扩容前已经达到最大,则直接将阈值设置为最大整数返回。

扩容的过程

 下面采用源代码+图片+文字描述的方式介绍HashMap的扩容过程。

  void addEntry(int hash, K key, V value, int bucketIndex) {      //数组扩容条件:1.已经存在的key-value mappings的个数大于等于阈值      //             2.底层数组的bucketIndex坐标处不等于null      if ((size >= threshold) && (null != table[bucketIndex])) {          resize(2 * table.length);//扩容之后,数组长度变了          hash = (null != key) ? hash(key) : 0;//为什么要再次计算一下hash值呢?          bucketIndex = indexFor(hash, table.length);//扩容之后,数组长度变了,在数组的下标跟数组长度有关,得重算。      }      createEntry(hash, key, value, bucketIndex);  }      void createEntry(int hash, K key, V value, int bucketIndex) {      HashMap.Entry<K, V> e = table[bucketIndex];      table[bucketIndex] = new HashMap.Entry<>(hash, key, value, e);      size++;  }

 上述addEntry方法中,如果size(当前容器内的元素个数)大于等于threshold(数组长度乘以负载因子),并且底层数组的bucketIndex坐标处不等于null,那么将会进行扩容(resize)。否则,不会进行扩容。

 下面将着重介绍一下扩容的过程:

        void resize(int newCapacity) {   //传入新的容量            Entry[] oldTable = table;    //引用扩容前的Entry数组            int oldCapacity = oldTable.length;            if (oldCapacity == MAXIMUM_CAPACITY) {  //扩容前的数组大小如果已经达到最大(2^30)了                threshold = Integer.MAX_VALUE; //修改阈值为int的最大值(2^31-1),这样以后就不会扩容了                return;            }                 Entry[] newTable = new Entry[newCapacity];  //初始化一个新的Entry数组            transfer(newTable);此行有遗漏,勘误见下面引用//!!将数据转移到新的Entry数组里            table = newTable;                           //HashMap的table属性引用新的Entry数组            threshold = (int) (newCapacity * loadFactor);此行有遗漏,勘误见下面引用//修改阈值        }

由wenni328博友修正:transfer(newTable); ==> transfer(newTable, initHashSeedAsNeeded(newCapacity));
threshold = (int) (newCapacity * loadFactor); ==> threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);

 扩容前首先获取扩容前数组的引用地址存进oldTable变量中,然后判断扩容前数组长度是否达到了int类型存储的最大值,如果是则放弃此次扩容,因为数组容量已经达到最大,无法再扩容了。

 下图为程序执行完Entry[] newTable = new Entry[newCapacity];代码之后的状态:

hashmap的扩容机制怎么理解

 这里就是使用一个容量更大的数组来代替已有的容量小的数组,transfer()方法将原有Entry数组的元素拷贝到新的Entry数组里。

        void transfer(Entry[] newTable) {            Entry[] class="lazy" data-src = table;                   //class="lazy" data-src引用了旧的Entry数组            int newCapacity = newTable.length;            for (int j = 0; j < class="lazy" data-src.length; j++) { //遍历旧的Entry数组                Entry<K, V> e = class="lazy" data-src[j];             //取得旧Entry数组的每个元素                if (e != null) {                    class="lazy" data-src[j] = null;//释放旧Entry数组的对象引用(for循环后,旧的Entry数组不再引用任何对象)                    do {                        Entry<K, V> next = e.next;                        int i = indexFor(e.hash, newCapacity); //!!重新计算每个元素在数组中的位置                        e.next = newTable[i]; //标记[1]                        newTable[i] = e;      //将元素放在数组上                        e = next;             //访问下一个Entry链上的元素                    } while (e != null);                }            }        }        static int indexFor(int h, int length) {            return h & (length - 1);        }

 newTable[i]的引用赋给了e.next,也就是使用了单链表的头插入方式,同一位置上新元素总会被放在链表的头部位置;这样先放在一个索引上的元素终会被放到Entry链的尾部(如果发生了hash冲突的话)。在旧数组中同一条Entry链上的元素,通过重新计算索引位置后,有可能被放到了新数组的不同位置上。

 下面会以图片的形式演示一下transfer的过程(下面图片的红色字体表示与上图有区别的地方,后面图片都是这样,后面红色字体说明不再赘述)

 下图为程序执行完class="lazy" data-src[j] = null;代码之后的状态(这是第一次循环时的状态):

hashmap的扩容机制怎么理解

 首先,将table[]数组的引用地址赋值给class="lazy" data-src[]数组。

 然后,Entry<K, V> e = class="lazy" data-src[j];是将class="lazy" data-src[j]位置的链表交给e变量存储。由于class="lazy" data-src[j]位置的链表已经交给e存储了,所以可以大胆的将class="lazy" data-src[j]=null;然后等待垃圾回收。

 下图为程序执行完Entry<K, V> next = e.next;代码之后的状态(这是第一次循环时的状态):

hashmap的扩容机制怎么理解

 这里先将e.next的值备份至next变量中,后续代码会将e.next的指向更改,所以在这里将e.next的值备份。

 下图为程序执行完e.next = newTable[i];代码之后的状态(这是第一次循环时的状态):

hashmap的扩容机制怎么理解

 由于newTable[3]的值为null,所以e.next为null,如上图所示。

 下图为程序执行完newTable[i] = e;代码之后的状态(这是第一次循环时的状态):

hashmap的扩容机制怎么理解

 下图为程序执行完e = next;代码之后的状态(这是第一次循环时的状态):

hashmap的扩容机制怎么理解

 如上述所示,Entry1这个节点成功插入到了newTable中,一轮循环结束时,因为判断e!=null,所以会再重复上述过程,直至所有节点移动到newTable中。

小结

  • 扩容是一个特别耗性能的操作,所以当程序员在使用HashMap的时候,估算map的大小,初始化的时候给一个大致的数值,避免map进行频繁的扩容。

  • 负载因子是可以修改的,也可以大于1,但是建议不要轻易修改,除非情况非常特殊。

  • HashMap是线程不安全的,不要在并发的环境中同时操作HashMap,建议使用ConcurrentHashMap。

  • JDK1.8引入红黑树大程度优化了HashMap的性能。

以上就是“hashmap的扩容机制怎么理解”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注编程网行业资讯频道。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

hashmap的扩容机制怎么理解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

hashmap的扩容机制怎么理解

今天小编给大家分享一下hashmap的扩容机制怎么理解的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。hashmap的扩容机制
2023-07-05

怎么理解List的扩容机制

这篇文章主要讲解了“怎么理解List的扩容机制”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么理解List的扩容机制”吧!一:背景1. 讲故事在前一篇大内存排查中,我们看到了Diction
2023-06-01

怎么用add方法理解ArrayList的扩容机制

这篇文章主要介绍“怎么用add方法理解ArrayList的扩容机制”,在日常操作中,相信很多人在怎么用add方法理解ArrayList的扩容机制问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么用add方法理
2023-07-05

add方法理解ArrayList的扩容机制

这篇文章主要为大家介绍了add方法理解ArrayList的扩容机制示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-03-07

go的切片扩容机制详解

本文主要介绍了go的切片扩容机制详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
2023-05-14

java arraylist扩容机制原理是什么

Java中的ArrayList是基于数组实现的动态数组,其扩容机制的原理如下:1. 初始容量:当创建一个ArrayList对象时,会分配一定的初始容量,例如10个元素的容量。2. 扩容策略:当ArrayList中的元素个数超过当前容量时,需
2023-10-19

Java ArrayList扩容机制原理是什么

本文小编为大家详细介绍“Java ArrayList扩容机制原理是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“Java ArrayList扩容机制原理是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。扩
2023-07-05

关于ArrayList的动态扩容机制解读

这篇文章主要介绍了关于ArrayList的动态扩容机制解读,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2022-11-13

go的切片扩容机制是什么

本篇内容主要讲解“go的切片扩容机制是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“go的切片扩容机制是什么”吧!切片的扩容策略?如何扩容?扩容策略:如果切片的容量小于 1024 个元素,于
2023-07-05

java中ArrayList集合的扩容机制是什么

这篇文章主要讲解了“java中ArrayList集合的扩容机制是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“java中ArrayList集合的扩容机制是什么”吧!1、扩容要看添加方法,
2023-06-20

怎么理解Java1.7中的HashMap源码

本篇内容主要讲解“怎么理解Java1.7中的HashMap源码”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么理解Java1.7中的HashMap源码”吧!存储结构内部包含了一个 Entry
2023-06-25

Namespace机制怎么理解

今天小编给大家分享一下Namespace机制怎么理解的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。NamespaceLinu
2023-06-15

阿里云服务器扩容后怎么样使用手机控制

如果您想使用阿里云服务器进行远程控制,您可以使用以下方法:使用AliDeviceManager:阿里云提供了一个AliDeviceManager工具,可以让您在阿里云服务器上远程管理您的智能设备。您可以在AliDeviceManager中选择一个需要控制的设备,然后输入您的设备号和密码,就可以远程控制该设备了。使用手机APP:现在有很多手机APP可以帮助您远程控制阿里云服务器。您可以在阿里云应用商店中下载这些APP,以便您在手机上使用它们。在手...
2023-10-27

Linux的I/O机制怎么理解

本篇内容主要讲解“Linux的I/O机制怎么理解”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Linux的I/O机制怎么理解”吧!你真的了解多线程吗?如果问你“为什么多线程可以提高程序运行效率?
2023-06-15

阿里云服务器扩容后怎么样使用手机控制器

一、使用手机控制器的目的使用手机控制器的目的主要有以下几点:实现云服务器的远程管理。使用手机控制器可以让用户通过手机APP或Web界面进行云服务器的远程管理和配置,包括设置服务器、添加节点、配置服务等操作。这样用户就可以在任何时间、任何地点、使用任何设备控制云服务器,从而实现云服务器的高效管理和优化。实现多人协同管理。
阿里云服务器扩容后怎么样使用手机控制器
2023-10-28

怎么给VMware虚拟机中的CentOS分区扩容

这篇文章主要讲解了“怎么给VMware虚拟机中的CentOS分区扩容”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么给VMware虚拟机中的CentOS分区扩容”吧!由于开发测试机器dev
2023-06-10

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录