HashMap源码逻辑
基本属性
Node<K,V>[] table //哈希表结构中数组的名字
DEFAULT_INITIAL_CAPACITY //数组默认长度16
DEFAULT_LOAD_FACTOR //默认加载因子0.75
HashMap里面每一个对象包含以下内容:
链表中的键值对对象
int hash; //键的哈希值
final K key; //键
V value; //值
Node<K,V> next;//下一个节点的地址值
红黑树中的键值对对象
int hash; //键的哈希值
final K key; //键
V value; //值
TreeNode<K,V> parent; //父节点的地址值
TreeNode<K,V> left; //左子节点的地址值
TreeNode<K,V> right; //右子节点的地址值
Boolean red; //节点的颜色
在添加元素时put方法底层调用的是putVal方法,该方法需要五个参数
哈希值 hash(key)
键 key由外界传入
值 value由外界传入
若键重复是否将老元素的值覆盖 onlyIfAbsent默认为false由Java设置
evict表示被覆盖的值是否丢弃(具体意义不明,从变量名的意义理解为该意)
在putVal中由局部变量node数组tab用于记录哈希表中数组的地址值,该变量用于节约内存,将全局变量tab的值赋给局部变量tab
局部变量Node类型P用来记录键值对对象的地址值
局部变量int类型n表示当前数组的长度
局部变量i表示索引
putVal中第一步将哈希表中数组tab的地址值赋值给局部变量tab,以下tab称为数组
随后进行判断数组tab是否为空如果为空那么Java底层会调用resize方法创建一个默认长度为 16,加载因子为0.75的数组,如果不为空会判断是否满足扩容条件(数组长度*加载因子),如果满足会将数组扩容为原先的两倍并把数据全部转移到新的哈希表中若不满足则在该判断中不进行任何操作,然后将新数组的地址值赋值给tab
判断过后会拿着数组的长度和键的哈希值进行计算,计算出当前键值对对象在数组中应存入的位置并赋值给i,然后获取数组中i索引位置的数据并赋值给p
随后进行判断p是否为空,如果为空那么底层会创建一个键值对对象将键的hash值、键、值、下一个节点的地址值
如果不为空则说明该索引已经有数据,需要再判断数组中元素的键和新传入元素的键的hash值是否相同,若键的hash值相同且数组中元素的键的值和新传入元素的键的值也相同,或者新传入元素的值不为空且新传入的键和数组中元素的键相等,那么将数组中元素的地址值赋值给Node类型变量e
如果判断为false再一次判断p(数组中的元素)是否是红黑树中的树节点,若是则调用方法putTreeVal方法把当前节点按照红黑树的规则添加到树中,putTreeVal方法原理和putVal方法大同小异但多了按照红黑树方法添加的判断
若前面两次判断都为false那么Java底层将按照数组中挂载链表的情况处理
建立一个for循环,在循环中判断当前数组中元素是否有下一个节点并将地址值赋值给e,如果为空说明该元素是链表中最后一个元素,那么会创建一个新节点并将新节点的地址值赋值给上一个节点中的next变量形成链表然后判断当前链表长度超过8,如果超过8会调用方法treeifBin,该方法底层还会判断数组长度是否大于64,如果满足两个条件就会将链表转成红黑树随后结束循环,如果不为空则判断下一个节点的哈希值是否等于新传入元素的哈希值且下一个节点的键是否等于新传入元素的键,或者新传入节点的键不为空且两个节点的键相同(该判断用于判断新传入的元素是否已经在数组中存在,第一次判断两个键是否相等后再一次判断是否相等是因为当键是字符串时第一次的判断比较的是地址值而相同的内容创建的字符串地址值是不同的,而第二次比较是调用的equals方法,避免了键重复的情况),若以上条件满足则跳出循环,若不满足则将下一个节点的地址值赋值给p进行下一次判断直到最后一个元素或者元素重复,随后该判断结束
然后判断e是否为空,若不为空则代表元素有重复(上面说过了e表示当前元素的下一个元素,若e不为空则表示出现了元素重复以至于跳出循环,若e为空则表示循环到了链表的最后而没有出现元素重复跳出循环的操作)此时需要执行覆盖操作,再次判断方法执行时onlyifAbsent变量是否为false(不保留)或者数组中的元素是否为空,满足则将新元素的值覆盖旧元素的值,然后调用afterNodeAccess方法将e传入(该方法在hashMap中方法体为空,是用于给子类LinkedHashMap类继承使用,我也不知到啥用)随后返回旧的值(这就是为什么put方法覆盖元素会返回被覆盖的值)判断结束
然后再一次判断数组的长度加一后是否大于哈希表的扩容时机,如果满足调用resize方法扩容不满足则什么都不作
最后返回null(表示没有覆盖任何元素)
resize方法中先判断数组是否为空,为空则创建一个长度 16默认因子为0.75的数组,不为空会判断是否满足扩容条件