hashmap在1.8中是以数组+链表+红黑树的结构实现的,在数组中的存放位置是用hash属性&长度-1 去寻址的(因为长度是2的幂,加上寻址方式,方便扩容,扩容两倍后数据不在原来位置就在原来位置+原长度),允许key为null,但是只能有一个;value可以null,不限个数。
hashmap无参构造初始化时啥没有,只有put时才会调用resize()才会有长度,默认16
一接口
实现了
Map<K,V>, Cloneable, Serializable 接口
继承
AbstractMap类
二 内部一些属性
DEFAULT_INITIAL_CAPACITY 默认长度,16,2的幂,原因是为了扩容(后面说),寻址碰撞
MAXIMUM_CAPACITY 最大长度,2^30=1 073 741 824
DEFAULT_LOAD_FACTOR = 0.75f; 负载因子
TREEIFY_THRESHOLD = 8; 链表向红黑树转化的阀值
UNTREEIFY_THRESHOLD = 6;红黑树转化成链表的阀值
Node<K,V>[] table; node的数组,存放具体的值,
threshold 负载数量,容量*负载因子
size 长度。数组里放了多少个值
MIN_TREEIFY_CAPACITY=64 转为红黑树的时候最小长度
数组
size 记录长度
modCount记录操作数
还有静态内部类Node,存储数据,key,value,hash,next(链表,指向下一个数据)
hash(),
计算hash值,获取key.hashCode()^(key.hashCode()>>>16)(异或操作,计算快,key 为null返回0)
put() ,
1.首先判断table是否已经初始化,否则调用resize(),进行初始化。
2.获取key对应位置的值,null就实例化一个新的node放进去,如果不null,或取得数据判断hash是否相等equals,是的话取得当前值,不是的话就要遍历了,(简单的说就是看看当前位置是不是没有hash冲突,当前位置不是链表也不是红黑树)当前位置空就直接放进去;如果是红黑树,调用putTreeVal()方法;已经有值的话就是个链表,当前位置node.next赋值为当前对象,并进行判断链表长度达到8,转化成红黑树;
3.判断(++size > threshold),根据条件进行判断是否需要扩容(2倍)
4.treeifyBin()
转化红黑树的时候,table长度最少要到64
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
resize();
resize()
扩容,
1.初始扩容,默认16
2.已经是最大长度了,不在扩容。
3..长度没达到最大长度,*2 也没达到最大长度。扩容两倍
扩容是指重新构造一个新的数组(两倍原数组),将原数组遍历重新分配到新数组里,因为是两倍扩容,并且长度时2的幂,寻址时又是获取node.hash&(容量-1),使得数据不在当前位置就在原位置的2倍的地方,减少移动。