当前位置: 首页 > 科技观察

HashMap中那些傻傻分不清楚的概念

时间:2023-03-14 23:48:08 科技观察

很多人都是通过阅读源码来学习Java的,这是一个很好的方式。JDK的源码自然是***的。在JDK的众多类中,我觉得HashMap及其相关类设计的比较好。很多人都看过HashMap的代码。不知道你是不是和我一样。你觉得HashMap里面和容量相关的参数太多了,分不清。其实本文介绍的内容比较简单,只要认真看一下HashMap的原理,还是可以理解的。之所以单独写一篇文章,是因为后面有好几篇HashMap源码分析的文章,对这些概念都不熟悉。如果是这样,接下来的文章就很难看下去了。HashMap中重要的成员变量首先看HashMap中定义了哪些成员变量。上图是HashMap中主要成员变量的示意图,其中一个是我们本文主要关注的:size、loadFactor、threshold、DEFAULT_LOAD_FACTOR和DEFAULT_INITIAL_CAPACITY。我们先简单解释一下这几个参数的含义,然后再分析它们的作用。HashMap类有以下主要成员变量:transientintsize;记录Map中KV对的数量;loadFactor加载印象以衡量HashMap的完整性。loadFactor的默认值为0.75f(staticfinalfloatDEFAULT_LOAD_FACTOR=0.75f;)。整数阈值;临界值,当实际KV数超过阈值时,HashMap会扩充容量,阈值=容量*负载因子HashMap中除了上述重要的成员变量外,还有一个与它们密切相关的概念:容量,如果不指定,默认容量为16(staticfinalintDEFAULT_INITIAL_CAPACITY=1<<4;)可能你看完还是一头雾水,size和capacity是什么关系?为什么要定义这两个变量。loadFactor和threshold在做什么?大小和容量HashMap中大小和容量的区别实际上很容易解释。我们知道HashMap就像一个“桶”,那么capacity就是这个桶“当前”所能容纳的最大元素个数,而size则表示这个桶已经装满了多少个元素。看下面的代码:Mapma??p=newHashMap();map.put("hollis","hollishuang");ClassmapType=map.getClass();Methodcapacity=mapType.getDeclaredMethod("capacity");capacity.setAccessible(true);System.out.println("capacity:"+capacity.invoke(map));Fieldsize=mapType.getDeclaredField("size");size.setAccessible(真的);System.out.println("大小:"+size.get(地图));我们定义一个新的HashMap,在里面放一个元素,然后通过反射打印容量和大小。输出结果为:capacity:16,size:1。默认情况下,一个HashMap的capacity是16。设计成16的好处在《全网把Map中的hash()分析的最透彻的文章,别无二家。》中也有简单介绍。主要原因是可以用按位AND代替取模来提高hash效率。为什么我刚才说容量是这个桶“当前”可以容纳的最大元素数?你现在怎么理解?其实HashMap是有扩容机制的。当一个HashMap第一次初始化时,它的容量默认是16。当达到扩容条件时,需要扩容,容量从16扩容到32。我们知道HashMap的一个重载构造函数是支持传入initialCapacity的,所以我们尝试设置一下看看结果。Mapma??p=newHashMap(1);map.put("hahaha","hollischuang");ClassmapType=map.getClass();Methodcapacity=mapType.getDeclaredMethod("capacity");capacity.setAccessible(true);System.out.println("capacity:"+capacity.invoke(map));Mapma??p=newHashMap(7);map.put("hahaha","hollischuang");ClassmapType=map.getClass();Methodcapacity=mapType.getDeclaredMethod("capacity");capacity.setAccessible(true);System.out.println("capacity:"+capacity.invoke(map));Mapma??p=newHashMap(9);map.put("hahaha","hollischuang");ClassmapType=map.getClass();Methodcapacity=mapType.getDeclaredMethod("capacity");capacity.setAccessible(true);System.out.println("capacity:"+capacity.invoke(map));分别执行以上3段代码,分别输出:容量:2、容量y:8,capacity:16也就是说默认情况下,HashMap的容量是16,但是如果用户通过构造函数指定一个数作为容量,那么Hash会选择大于该数的2的第1次方作为容量。容量。(1->2,7->8,9->16)这里有个小建议:在初始化HashMap的时候,尽量指定它的大小。尤其是当您知道地图中存储的元素数量时。(《阿里巴巴Java开发规约》)loadFactor和threshold前面我们提到HashMap有一个扩容机制,就是当扩容条件满足时,它会扩容,从16扩容到32、64、128……那么,这个是做什么用的呢?扩张条件是指?羊毛布?其实HashMap的扩容条件是当HashMap中的元素个数(size)超过阈值(threshold)时,会自动扩容。在HashMap中,threshold=loadFactor*capacity。loadFactor是加载因子,表示HashMap的填充度。默认值为0.75f。设置为0.75有个好处,就是0.75正好是3/4,capacity是2的幂,所以两个数的乘积是整数(capacity为2也是一样)。对于一个默认的HashMap,默认情况下,当其大小大于12(16*0.75)时会触发扩容。验证代码如下:Mapma??p=newHashMap<>();map.put("hollis1","hollischuang");map.put("hollis2","hollischuang");map.put("hollischuang")","hollischuang");map.put("hollis4","hollischuang");map.put("hollis5","hollischuang");map.put("hollis6","hollischuang");map.put("hollis7","hollischuang");map.put("hollis8","hollischuang");map.put("hollis9","hollischuang");map.put("hollis10","hollischuang");地图。put("hollis11","hollischuang");map.put("hollis12","hollischuang");ClassmapType=map.getClass();Methodcapacity=mapType.getDeclaredMethod("capacity");capacity.setAccessible(true);System.out.println("capacity:"+capacity.invoke(map));Fieldsize=mapType.getDeclaredField("size");size.setAccessible(true);System.out.println("size:"+size.get(map));Fieldthreshold=mapType.getDeclaredField("threshold");threshold.setAccessible(true);System.out.println("threshold:"+threshold.get(map));FieldloadFactor=mapType.getDeclaredField("loadFactor");loadFactor.setAccessible(true);System.out.println("loadFactor:"+loadFactor.get(map));地图。put("hollis13","hollischuang");Methodcapacity=mapType.getDeclaredMethod("capacity");capacity.setAccessible(true);System.out.println("capacity:"+capacity.invoke(map));Fieldsize=mapType.getDeclaredField("size");size.setAccessible(true);System.out.println("size:"+size.get(map));Fieldthreshold=mapType.getDeclaredField("threshold");threshold.setAccessible(true);System.out.println("threshold:"+threshold.get(map));FieldloadFactor=mapType.getDeclaredField("loadFactor");loadFactor.setAcaccessible(true);System.out.println("loadFactor:"+loadFactor.get(map));输出结果:capacity:16size:12threshold:12loadFactor:0.75capacity:32size:13threshold:24loadFactor:0.75当HashMap中的元素个数达到13时,容量从16扩容到32。HashMap也提供了支持输入的方法两个参数,initialCapacity和loadFactor,用于初始化容量和负载因子。但是一般不建议修改loadFactor的值。综上所述,HashMap中的size表示当前有多少个KV对,capacity表示HashMap当前的容量。默认值为16,每次扩展加倍。loadFactor是加载因子。当Map中的元素个数超过loadFactor*capacity的值时,就会触发扩容。loadFactor*容量可以用threshold表示。【本文为专栏作家霍利斯原创文章,微信公众号Hollis(ID:hollishuang)撰文】点此阅读更多本作者好文