1.7
在new HashMap() 的时候,不会初始化map,在对map进行put的时候才会初始化,初始化数组长度为传参initCapacity最邻近的一个2的n次方的数,如initCapacity=519时,数组长度为1024。
在第一次put值的时候,会进行初始化,步骤为
①计算数组长度,保证数组长度为2的n次方。
②计算扩容阈值,数组长度*负载因子。
③初始化数组,new Entry[capacity];
④判断是否需要hashSeed
每次会加在链表头部
当size大于阈值并且当计算出当前元素所属下标有值的时候才会进行resize();
还有当调用putAll时,参数map的size大于对象的阈值时,计算出参数map的capacity,并增大当前对象capacity,每次左移一位,直到thisCapacity>targetCapacity,最终如果thisCapacity大于table.length则会进行resize();
resize():
①进行判断,是否已经扩充到最大了,如果时则将阈值设置为int最大值。
②new一个新的数组 new Entry[newCapacity];
③将原来的数组值迁移到新的数组中;
遍历旧的数组,如果需要rehash则将数组中原来的每个元素rehash,并indexFor分散到newTable的各个index中
④计算新的阈值
clear方法调用Arrays.fill();将数组元素设为null。
判断contains,遍历数组+链表,找出对应值,如果存在则为true
clone()返回实例的浅拷贝,键和值本身没有拷贝。
1.8
resize()
判断旧容量如果大于0,说明map已经有元素到临界值需扩容
并且如果已经>=最大容量,增将阈值设置为int最大值返回;
如果旧容量两倍小于最大容量并且旧容量>=默认初始容量,则新阈值=旧阈值*2;
如果旧容量=0 且旧阈值大于0,说明map还没有put值,只是带参数new出来而已,则新容量=旧阈值
如果旧容量=0旧阈值也=0,则为无参new出来,初始化 新容量和阈值都为默认值
前面都走过了,如果新阈值=0,则计算新容量*加载因子 为新阈值,并赋值给属性;
创建新数组
如果旧数组不为空,则为扩容,需迁移元素
遍历旧数组,如果数组元素中只有一个node,则直接赋值到新数组的indexFor中,
如果元素是treenode 则调用split方法
其他则是链表
重新计算indexFor
//jdk1.8 map.put
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
//如果table为null 则初始化table
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//如果计算出的下标元素为null,就直接new个节点
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
//如果不为空 判断 key是否相等 相等说明存在 则直接替换value
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
//如果当前节点属于treeNode 则调用方法 创建树节点(存在则返回节点,不存在则创建新节点,返回null)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
//链表 遍历链表看是否存在key
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
//如果p下一个节点为空,则创建新节点 赋值
p.next = newNode(hash, key, value, n