经常用HashMap,这篇博客来研究一下它的源代码结构。
之所以叫HashMap,从名字上能够看出来,它是一个Map的同时还是实现了Hash表的数据结构。
所谓Hash表,就是散列,这种数据结构的优点是能够具备数组的快速查询的优点又能融合链表方便快捷的增加删除元素的优势。
HashMap集成了AbstractMap,实现了Map接口。
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable
HashMap的属性:
- DEFAULT_INITIAL_CAPACITY 这个属性代表HashMap默认的初始容量;
- MAXIMUM_CAPACITY 这个属性代表HashMap的最大容量;
- DEFAULT_LOAD_FACTOR 这个属性代表的HashMap容量默认的调整因子,这个在后面的构造函数就能够知道它的用处;
- Entry[] table 这个属性是一个Entry数组,是用来存储数据的;
- size 这个属性是HashMap的大小;
- threshold 这个属性是调整大小的极限值,和调整因子和容量有关系,后面会详细介绍;
- loadFactor 这个属性是调整因子;
- modCount 这个属性是记录HashMap被改变的次数。
这里先知道这些属性是做什么的,肯能光看这些文字解释会云里雾里的,后面在一些方法中用到在详细介绍就会明白他们具体作用了。
来看一下HashMap的构造函数:
先看前三个构造函数,主要做的就是创建空的HashMap,可以用户自定义调整因子,也可以没有传入调整因子,在这个过程中要初始化threshold,和Entry数组。其实后面的所有方法,例如put、get、remove等都是在操作这些参数。
在最后一个构造函数来说主要是实现通过传入一个map来创建一个HashMap,这里容量是进行了一个max计算,主要是计算默认容量和m的容量取的最大值来作为容量。
在介绍方法之前还需要看一下Entry数组。这里涉及到了HashMap中的一个内部类Entry<K,V>,这个类是实现Map中的Map.Entry接口的。
这个类应该很好明白,就是获取key,获取值,设置值,判断两个Entry是否相等,还有一个获取hashCode方法。
看完了Entry代码之后来看看最常用的put方法(其他方法后面再介绍),这个方法就能够看到HashMap内部是如何操作上面的哪些hashCode、调整因子、调整极限等。
put方法代码:
首先看一下key!=null的情况,首先进行了hash()方法,这个方法进行了下面的操作
返回了一个int值,这个操作主要是为了避免了用哈希表产生key冲突问题。
之后又调用了indexFor方法,这个方法是进行了一个hash方法返回值和Entry数组长度做与运算,这里做与运算主要是为了避免hash值算出来之后可能会出现数组越界(这一个方法的作用是在网上找到了,这块我也没有搞清楚,如果有知道的可以给我留言指正),当执行完这一步之后就开始进行遍历去找链表中是否有此key,如果有则进行链表对应的key赋值,如果没有则将modCount++操作,此属性前面已经介绍了,它是记录修改次数,最后进行addEntry操作:
此方法直接将根据传进来的参数生成Entry赋给table数组中的相应值,后面进行了判断,当插入一个key的时候会对size进行++,之后和调整极限值做比较,如果size++大于极限,则通过resize方法来进行调整数组大小。
具体调整方法:
其实比较实现比较清晰,就是新创建一个Entry数组,这个数组长度为调整后的长度,将旧的数组的值赋给新的Entry即可。
赋值的操作为transfer方法:
到这里前面介绍的那些属性应该知道是做什么的了,其实知道了put方法,其他方法也就简单多了,后面博客再来介绍吧。