HashMap1.8源码解读(细品版)

HashMap作为最常用集合框架,我们应该知道它的组成部分是什么,使用场景是什么,这样我们才能更加合适地使用它
大家都知道(顶级)接口Map的数据结构是key:value,而HashMap作为Map的子类,自然也是以键值对的形式存在。接下来,让我们来看看hashmap里面有什么我们需要注意的地方

基础知识

  1. 集成自AbstractMap,实现了Cloneable,Serializable接口。 那么就有Object.clone的对象拷贝、还有序列化的能力
public class HashMap<K,V> extends AbstractMap<K,V]]>     implements Map<K,V>, Cloneable, Serializable 
  1. 默认容量大小2的4次方 -> 16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
  1. 最大容量 2的30次方≈ 10.7亿
static final int MAXIMUM_CAPACITY = 1 << 30; 
  1. 加载因子默认0.75
static final float DEFAULT_LOAD_FACTOR = 0.75f; 
  1. 树化阈值,hashmap在数量结构达到一定规模的情况下,会发生结构变化。我们都知道hashmap1.8以后引入了红黑树的概念,这玩意就跟红黑树有关哈
static final int TREEIFY_THRESHOLD = 8;  
  1. 取消树化阈值,当长度小于6时,树形结构将转变为链表结构
static final int UNTREEIFY_THRESHOLD = 6;  
  1. 最小树形化容量阈值,即哈希表中的容量大于64时,才允进行树形化
static final int MIN_TREEIFY_CAPACITY = 64;
  1. Hashmap数据如何存储?
    先下一个定论, hashmap是一个数组+链表的存储结构。
    首先说说数组体现在哪里:hashmap底层存储的是Node[] 结构,当调用.put(key,value),首先会根据key计算出一个hash值(这里我们暂时先称之为hash),数组当前容量假设为capacity
    那么Node[]的存储位置计算方式为:
    i = (capacity - 1) & hash
    tab[i] = new Node(key,value);
/**
* 可以参考一下代码
*/
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    ...
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    ...
    return null;
}

HashMap1.8源码解读(细品版)_第1张图片

此时存储另一个元素.put(key1,value1)
假如key1通过hash计算出来的结果跟key的hash值一样,那么势必它们最后计算出来的存储位置会一样,都为3.
假设key1为节点node1,key0为节点node0。(key1!=key0)
node1不会存储在Node[]数组中,而是追加到了node0节点后面形成链表。
看下Node的数据结构

static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next;
        ...
}

Node存储了下一个Node元素的位置,来看看图片长什么样
HashMap1.8源码解读(细品版)_第2张图片

再看看程序中是如何计算的

// 贴部分源码哈,地方不够凑合看
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
   ...省略
    for (int binCount = 0; ; ++binCount) {
        if ((e = p.next) == null) {
            p.next = newNode(hash, key, value, null);
            if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                treeifyBin(tab, hash);
            break;
        }
        if (e.hash == hash &&
            ((k = e.key) == key || (key != null && key.equals(k))))
            break;
        p = e;
    }
    ...省略
}

稍微做点解释,p为Node[]数组中的节点,即本案例的node0,新加入的节点node1会通过这行代码追加到node0的下个节点,形成链表

p.next = newNode(hash, key, value, null);
// 对应本案例的
Node node1 = newNode(hash,key,value,null);
node0.next = node1;

注意一下这行代码哈treeifyBin(tab, hash);
默认的,我们都知道当node链表的数量大于8时,链表会转化成红黑树,但是还有一个条件不要忘记,就是数组的长度要大于MIN_TREEIFY_CAPACITY 最小数化阈值,否则node数组只会resize。 理论上,假如所有存储的key计算出来的hash值都相同,那么链表长度最大可以到达 MIN_TREEIFY_CAPACITY-1,也就是63的长度。再大就会大于树化阈值,变成红黑树。

你可能感兴趣的:(java,hashmap,java,数据结构)