Java 集合源码-HashMap 1.8

202 阅读6分钟

HashMap 的属性


/**
 * 默认的初始容量为 16
 */
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;

/**
 * 最大的容量 = 2^30
 */
static final int MAXIMUM_CAPACITY = 1 << 30;

/**
 * 默认的装载因子
 */
static final float DEFAULT_LOAD_FACTOR = 0.75f;

/**
 * 桶中的元素树化的阈值
 */
static final int TREEIFY_THRESHOLD = 8;

/**
 * 桶中的元素去树化的阈值
 */
static final int UNTREEIFY_THRESHOLD = 6;

/**
 * 最小树化的桶容量
 */
static final int MIN_TREEIFY_CAPACITY = 64;

/**
 * 数组,又叫作桶(bucket),存储 map 中数据
 */
transient Node<K,V>[] table;

/**
 * 作为entrySet()的缓存
 */
transient Set<Map.Entry<K,V>> entrySet;

/**
 * 元素的数量
 */
transient int size;

/**
 * 修改次数,用于在迭代的时候执行快速失败策略
 */
transient int modCount;

/**
 * map 扩容的阈值:size > threshold,map 自动扩容,threshold = capacity * loadFactor
 */
int threshold;

/**
 * 装载因子
 */
final float loadFactor;

java.util.HashMap#tableSizeFor 方法

/**
 * 
 * 该方法是为了 map 在初始化或扩容时,容量取离 cap 最近的 2^n 的值
 * 
 */
static final int tableSizeFor(int cap) {
    // 防止在 cap 已经是 2^n 的情况下,经过运算后得到的结果是 cap * 2
    // 例如 n = l6(0001 0000),经过下面运算之后,n = 0001 1111
    // 此时最后一步 n + 1 执行之后,此时的值就不是里 cap 最近的 2^n(16) 的值,而是 cap * 2(32)
    int n = cap - 1;
    n |= n >>> 1;
    n |= n >>> 2;
    n |= n >>> 4;
    n |= n >>> 8;
    n |= n >>> 16;
    // 如果 n 小于 0,返回 1,如果 n 大于最大容量,将不会再扩容,否则返回 n + 1
    return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}

java.util.HashMap#put 方法

/**
 * 将指定值与 map 中的指定键关联。如果 key 存在,value 替换原来的值
 *
 * @param key 指定的 key
 * @param value 指定 key 的值
 * @return 如果 key 存在,返回之前的值,如果 key 不存在,则返回null。
 */
public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

java.util.HashMap#putVal 方法

/**
 * 实现 map 和相关方法.
 *
 * @param hash key 的 hash 值
 * @param key 插入 map 的 key
 * @param value 值
 * @param onlyIfAbsent 如果为 true,不改变之前 key 的值
 * @param evict 如果为 false,则 table 处于 creation mode。
 *              该值对 HashMap 没有作用,对 LinkedHashMap 有作用
 * @return 返回之前 key 的值,如果没有返回 null 
 */
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    // tab:当前 map 的值,p:key 对应桶的位置存储的值(链表或树)
    // n:map.table 的长度,i:key 对应桶的索引值
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)
        // 如果桶为 null 或桶的大小为 0,初始化 map
        n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == null)
        // 如果 key 对应的桶为 null,新建一个节点放在桶中
        tab[i] = newNode(hash, key, value, null);
    else {
        // key 对应的桶不为 null
        // e 记录已存在 key 的 node
        // k 记录 e.key
        Node<K,V> e; K k;
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            // 桶中第一个元素的 key 和 新插入的 key相等,
            e = p;
        else if (p instanceof TreeNode)
            // 桶中的元素类型是 TreeNode (红黑树)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {
            // 桶中元素类型为 Node (链表)
            // binCount记录遍历链表节点的数量
            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
                        // binCount 大于等于树化的阈值,需要树化
                        treeifyBin(tab, hash);
                    break;
                }
                // 如果待插入的 key 在链表中存在了,则退出循环
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        if (e != null) { // map 中存在 key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                // 如果之前的值为 null 或者 onlyIfAbsent 为 false,替换旧值
                e.value = value;
            // HashMap 中未实现该方法
            afterNodeAccess(e);
            // 返回之前的值
            return oldValue;
        }
    }
    ++modCount;
    if (++size > threshold)
        // 元素大小大于阈值时,自动扩容
        resize();
    // HashMap 中未实现该方法
    afterNodeInsertion(evict);
    return null;
}
/**
 * 获取到 key 的 hash 值 无符号右移 16 位并和自己进行异或运算,
 * 这样做的目的是为了让高位与低进行混合,让两者都参与运算
 * 以便让 hash 值分布更加均匀、分散
 */
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

java.util.HashMap#resize 方法

/**
 * 
 * 初始化或两倍增加 table 的大小。
 *
 * @return the table
 */
final Node<K,V>[] resize() {
    // oldTab-旧数组,oldCap-旧 table 容量,oldThr-旧扩容阈值
    Node<K,V>[] oldTab = table;
    int oldCap = (oldTab == null) ? 0 : oldTab.length;
    int oldThr = threshold;
    // newCap-新容量 newThr-新扩容阈值
    int newCap, newThr = 0;
    if (oldCap > 0) {
        if (oldCap >= MAXIMUM_CAPACITY) {
            // 如果旧容量达到了 MAXIMUM_CAPACITY,则不再进行扩容
            threshold = Integer.MAX_VALUE;
            return oldTab;
        }
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                 oldCap >= DEFAULT_INITIAL_CAPACITY)
            // 如果 oldCap << 1 小于最大容量
            // 并且 oldCap 大于 DEFAULT_INITIAL_CAPACITY
            // 则容量扩大为两部,扩容门槛也扩大为两倍
            newThr = oldThr << 1; // double threshold
    }
    else if (oldThr > 0) 
        // 使用非默认构造方法创建的 map,第一次插入元素会走到这里
        // oldCap == 0 且 oldThr > 0
        // 把 newCap 设为 oldThr
        newCap = oldThr;
    else {
        // 调用默认构造方法创建的 map,第一次插入元素会走到这里
        // oldCap == 0 且 oldThr >== 0,说明还未初始化过
        // 则 newCap 为默认容量,newThr 为默认容量 * 默认装载因子
        newCap = DEFAULT_INITIAL_CAPACITY;
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
    }
    if (newThr == 0) {
        // 未对 newThr 设置,需要初始化 newThr
        // 如果 newCap 和 newCap * loadFactor 都小于 MAXIMUM_CAPACITY
        // 此时 newThr = newCap * loadFactor,否则等于 Integer.MAX_VALUE
        float ft = (float)newCap * loadFactor;
        newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                  (int)ft : Integer.MAX_VALUE);
    }
     // 重新设置 threshold 大小
    threshold = newThr;
    // 新 table 的大小为 newCap
    @SuppressWarnings({"rawtypes","unchecked"})
    Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
    // 重新设置 table 为 newTab
    table = newTab;
    if (oldTab != null) {
        // 旧 table 不为空,需要将之前的值迁移到新 table 中,重新计算索引值
        for (int j = 0; j < oldCap; ++j) {
            Node<K,V> e;
            if ((e = oldTab[j]) != null) {
                // 旧桶中元素不为空
                oldTab[j] = null;
                if (e.next == null)
                    // 桶中只有一个元素
                    // 计算它在新桶中的位置并把它搬移到新桶中
                    newTab[e.hash & (newCap - 1)] = e;
                else if (e instanceof TreeNode)
                    // 桶中的元素类型为 TreeNode(红黑树)
                    // 把这颗树打散成两颗树插入到新桶中去
                    ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                else { 
                    // 桶中的元素类型为 Node(链表),且链表的大小大于 2
                    // 分化成两个链表插入到新的桶中去
                    // 如:oldCap = 4,3、7、11、15 这四个元素都在 3 桶中
                    // newCap = 8,则 3、11 还是在 3 号桶 lo
                    // 7、15 要搬移到 7 桶中去 hi
                    // 分化成了两个链表 lo 和 hi
                    Node<K,V> loHead = null, loTail = null;
                    Node<K,V> hiHead = null, hiTail = null;
                    Node<K,V> next;
                    // 遍历链表
                    do {
                        next = e.next;
                        if ((e.hash & oldCap) == 0) {
                            // 元素在旧桶中的位置
                            if (loTail == null)
                                // 链表头节点
                                loHead = e;
                            else
                                // 新的节点插入到链表最后
                                loTail.next = e;
                            // 链表尾节点
                            loTail = e;
                        }
                        else {
                            if (hiTail == null)
                                hiHead = e;
                            else
                                hiTail.next = e;
                            hiTail = e;
                        }
                    } while ((e = next) != null);
                    // 遍历完成分化成两个链表了
                    if (loTail != null) {
                        loTail.next = null;
                        // 低位链表在新桶中的位置与旧桶一样
                        newTab[j] = loHead;
                    }
                    if (hiTail != null) {
                        hiTail.next = null;
                        // 高位链表在新桶中的位置正好是原来的位置加上 oldCap
                        newTab[j + oldCap] = hiHead;
                    }
                }
            }
        }
    }
    return newTab;
}

java.util.HashMap.TreeNode#putTreeVal 方法

/**
 * Tree version of putVal.
 */
final TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab,
                               int h, K k, V v) {
    // key 的比较器
    Class<?> kc = null;
    // 标记是否检索到 key 的节点
    boolean searched = false;
    // 设置树的根节点
    TreeNode<K,V> root = (parent != null) ? root() : this;
     // 从树的根节点开始遍历
    for (TreeNode<K,V> p = root;;) {
        // dir 标记是在左边还是右边,-1 左边,1 右边
        // ph = p.hash,当前节点 p 的hash
        int dir, ph; 
        // pk = p.key,当前节点 p 的key
        K pk;
        if ((ph = p.hash) > h)
            // h < p.hash,在左边
            dir = -1;
        else if (ph < h)
            // h > p.hash,在右边
            dir = 1;
        else if ((pk = p.key) == k || (k != null && k.equals(pk)))
            // p.hash == h && key 相等,说明 key 已存在,直接返回该节点
            // 回到 putVal() 中判断是否需要修改其 value 值
            return p;
        else if ((kc == null &&
                  (kc = comparableClassFor(k)) == null) ||
                 (dir = compareComparables(kc, k, pk)) == 0) {
            if (!searched) {
                // 以上未找到 k
                TreeNode<K,V> q, ch;
                searched = true;
                if (((ch = p.left) != null &&
                     (q = ch.find(h, k, kc)) != null) ||
                    ((ch = p.right) != null &&
                     (q = ch.find(h, k, kc)) != null))
                     // 遍历左右子树找到了直接返回
                    return q;
            }
            // 如果两者类型相同,再根据它们的内存地址计算hash值进行比较
            dir = tieBreakOrder(k, pk);
        }

        TreeNode<K,V> xp = p;
        if ((p = (dir <= 0) ? p.left : p.right) == null) {
            Node<K,V> xpn = xp.next;
            // 如果最后确实没找到对应key的元素,则新建一个节点
            TreeNode<K,V> x = map.newTreeNode(h, k, v, xpn);
            // 根据 dir 判断插入位置
            if (dir <= 0)
                xp.left = x;
            else
                xp.right = x;
            // 设置当前节点 p 的 next 指针的值为 x
            xp.next = x;
            // 设置新节点 x 的父节点为 xp
            // 设置新节点 x 的 pre 指针的值为 xp
            x.parent = x.prev = xp;
            if (xpn != null)
                // 当前节点 p 的 next 不(xpn)为空,需要将 xpn 的 pre 指针设为新节点 x 
                ((TreeNode<K,V>)xpn).prev = x;
            // 插入后,调用 balanceInsertion 方法,平衡红黑树
            // 把平衡后的 root 节点移动到链表的第一个节点
            moveRootToFront(tab, balanceInsertion(root, x));
            // 跳出循环,返回
            return null;
        }
    }
}