当前位置: 首页 > 后端技术 > Java

HashMap源码分析详解(上)

时间:2023-04-02 01:29:43 Java

jdk版本:1.8数据结构:HashMap底层主要基于数组+链表/红黑树实现。数组的优点是查询块。HashMap通过计算哈希码获取数组的下标来查询数据。同样,也可以通过哈希码得到数组下标来存储数据。为了解决哈希表中的冲突,HashMap采用了链表的方式,添加的数据存储在链表中。如果发送冲突,则将数据放在链表的末尾。上图左边部分是哈希表,也称为哈希数组(hashtable)://tablearraytransientNode[]table;表数组的引用类型是Node节点,数组中的每一个元素都是单向链表的头节点。链表主要是为了解决上面提到的哈希冲突。Node节点包含:hashhashvaluekeykeyvaluevaluenextnext指针Node节点结构如下:staticclassNodeimplementsMap.Entry{finalinthash;最后的K键;V值;下一个节点;Node(inthash,Kkey,Vvalue,Nodenext){this.hash=hash;this.key=键;this.value=值;这个.下一个=下一个;}//省略get/set等方法}mainattribute//存储元素数组Node[]table;//元素个数intsize;//数组扩容的临界值,计算为:元素容量*loadfactorintthreshold//负载因子,默认0.75floatloadFactor;//当链表长度为8时,转为红黑树intTREEIFY_THRESHOLD=8;//当长度为6时,它将从红黑树转换为链表。intUNTREEIFY_THRESHOLD=6;size记录了元素个数的阈值,也就是扩容的阈值,等于元素容量*加载因子TREEIFY_THRESHOLD8。当链表个数增加到8个时,转为红黑树UNTREEIFY_THRESHOLD6当链表的个数减少到6时,会退化为一个链表loadFactor。加载因子默认为0.75。loadFactor加载因子等于扩展阈值/数组长度,表示元素被填充。哈希冲突的概率增加,链表越长,查询效率越低。哈希冲突越低,数据查询效率越高。但是,空间利用率越低,空间越无用,容量会不断扩大。为了平衡查询时间和空间的使用,系统默认负载因子为0.75。获取哈希数组下标增删查查方法都需要先获取哈希数组的下标位置,先通过哈希算法计算出哈希值,然后进行长度取模得到元素的数组下标。首先是调用hash方法计算哈希值:staticfinalinthash(Objectkey){inth;返回(键==空)?0:(h=key.hashCode())^(h>>>16);}先获取hashCode值,然后进行高位运算,高位运算后的数据,再在a处进行取模运算更快的速度。计算出hash值后,进行取模运算:hash上面的(n-1)&n为长度,计算结果为数组的下标。构造方法HashMap()/***默认初始容量(16)*默认加载因子(0.75)。*/publicHashMap(){this.loadFactor=DEFAULT_LOAD_FACTOR;}将默认负载因子设置为0.75,将默认容量设置为16。HashMap(intinitialCapacity)//指定初始值大小publicHashMap(intinitialCapacity){this(initialCapacity,DEFAULT_LOAD_FACTOR);}//指定初始值和默认加载因子0.75publicHashMap(intinitialCapacity,floatloadFactor){if(initialCapacity<0),thrownewIllegalArgumentException("非法初始容量:"+initialCapacity);如果(initialCapacity>MAXIMUM_CAPACITY)initialCapacity=MAXIMUM_CAPACITY;if(loadFactor<=0||Float.isNaN(loadFactor))thrownewIllegalArgumentException(loadFactorfactor:"+loadFactor);this.loadFactor=loadFactor;this.threshold=tableSizeFor(initialCapacity);}HashMap(intinitialCapacity)指定初始容量,调用HashMap(intinitialCapacity,floatloadFactor)其中loadFactor默认为0.75,首先检查容量,如果小于0报错,如果大于分配给的最大容量maximumcapacity.然后检查loadingfactor,如果小于0或者非数,会报错tableSizeFor使用右移和OR运算,保证capacity是2的幂,并且是传入2,返回传入的数据。传入不是2的幂的数据,返回一个大于传入的数据且接近2的幂的数。例如:传入10,返回16。传入21,返回32。#HashMap(Mapm)publicHashMap(Mapm){this.loadFactor=DEFAULT_LOAD_FACTOR;putMapEntries(m,false);}将采集到的m个数据添加到HashMap集合中,首先设置默认加载因子,然后调用putMapEntries将集合元素添加到HashMap中。putMapEntries遍历数组并添加数据。总结本文分析了基于jdk1.8的HashMap源码,主要介绍:HashMap是基于数组+链表/红黑树结构实现的。链表方法用于解决散列冲突。Node节点记录了数据的key、hash、value和next指针。HashMap主要属性:size元素个数table[]hash数组threshold扩展thresholdloadFactor负载因子TREEIFY_THRESHOLD8,链表个数为8,转化为红黑树。UNTREEIFY_THRESHOLD6,链表个数为6,红黑树转为链表。对元素进行增删改查,必须先获取数组下标。HashMap首先调用hasCode方法,hashCode()的高16位与低16位异或,大大提高了计算速度。然后对数组的长度进行模运算。本质就是取key的hashCode值,高阶运算,取模运算。HashMap的几种构造方法:HashMap()设置默认负载因子0.75,默认容量16。HashMap(intinitialCapacity)设置初始容量。默认加载因子为0.75。容量必须是2的次方,如果不是2的次方,增加到接近2的次方。HashMap(Mapm)主要遍历添加的集合,添加数据。参考《SimpleHashMapDesignandOptimizationJava8seriesRe-qualitywithHashMap》,如果觉得不错,请点个赞!