天天看点

JDK1.7版本HashMap的源码分析JDK1.7版本HashMap

注意:HashMap的源码本文仅分析JDK1.7,1.8版本的请参考: JDK1.8版本HashMap

JDK1.7版本HashMap

1 结构图

JDK1.7版本HashMap的源码分析JDK1.7版本HashMap

HashMap由数组(本文后续以table表示)+ 链表构成。其本意是用table来存储一个键值对(本文后续以Entry表示),然后可以通过key取hash值同时对数组长度取模,散列分布到数组每个下标下的时候,那取值时间复杂度将为O(1),在最理想的情况下,就是数组一个下标上只存一个Entry值。

但是天不如人愿,并不存在一个如此优秀的Hash函数,让每个Entry的key一定能散列分布在数组每个下标下,总是会出现如下情况:不同的key值,取hash值并取模后,数组下标值一样,那这个时候就出现了Hash冲突。解决Hash冲突方法有挺多种的,而HashMap采用链表来解决,意思是,假如出现Hash冲突后,则在该下标下,形成一个单链表,存储不同的Entry,那么在查询操作的时候,就需要对链表进行遍历,同时调用HashMap的key的equals方法,判断其是否相等,相等则返回该Entry,这也就是HashMap为什么要求key对象一定要实现hashCode方法和equals方法的原因。

2 继承关系

我们来看下HashMap的继承关系图:

JDK1.7版本HashMap的源码分析JDK1.7版本HashMap

左边实现的Cloneable接口和Serializable接口是为了实现克隆和序列化功能,非本文重点关注对象,请忽略。HashMap主要继承父类AbstractMap,而父类实现了Map接口。

2.1 Map接口

下面是Map接口提供的方法:

JDK1.7版本HashMap的源码分析JDK1.7版本HashMap

可以看到Map还有一个内部接口Entry,Entry就是代表键值对,一个key和一个vale值。

2.2 AbstractMap抽象类(非重点父类,请稍微浏览即可)

AbstractMap对于HashMap的作用,仅仅是提供了keySet 和values 变量。而其他的Map方法都在HashMap中重写了一遍。因此请看淡AbstractMap抽象父类。

JDK1.7版本HashMap的源码分析JDK1.7版本HashMap

AbstractMap.java(省略了其实现的方法):

public V put(K key, V value) {
        throw new UnsupportedOperationException();
    }
    ...
    public abstract Set<Entry<K,V>> entrySet();
           

以上两个方法是AbstractMap未实现Map接口的方法。其它方法,AbstractMap基本都实现了。

2.3 HashMap类

HashMap虽然继承了AbstractMap类,但实际上,它基本上重写了AbstractMap类实现Map接口的方法,换句话说,他表面上继承AbstractMap,但暗地里把它实现的方法都重写了一遍,丝毫不留情面。其只使用了父类的两个成员变量keySet和values

JDK1.7版本HashMap的源码分析JDK1.7版本HashMap

2.3.1 字段

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

    /**
     * 最大容量值
     */
    static final int MAXIMUM_CAPACITY = 1 << 30;

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

    /**
     * 空table
     */
    static final Entry<?, ?>[] EMPTY_TABLE = {};

    /**
     * table,用来存放键值对,键值对可以单个值,也可以是一个链表
     */
    transient Entry<K, V>[] table = (Entry<K, V>[]) EMPTY_TABLE;

    /**
     * 大小
     */
    transient int size;

    /**
     * 阈值,扩容的主要判断条件之一
     */
    int threshold;

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

    /**
     * 结构更改次数的统计,增、删都会加1。也是快速失败的触发依据,后面HashIterator会介绍如何使用该值
     */
    transient int modCount;

    /**
     * 默认备选hash阈值
     */
    static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE;

    /**
     * 注意、注意、注意:
     * 这两个成员变量来自父类AbstractMap,由于其为default类型,在不同包将访问不到,因此在此声明,以此代替
     * 由于keySet、value在父类中的keySet()、values()、clone()方法中用到,而HashMap这几个方法全部重写了,因此不会影响使用
     */
    transient volatile Set<K> keySet = null;
    transient volatile Collection<V> values = null;
           

2.3.2 构造器

最重要的构造器:

public HashMap(int initialCapacity, float loadFactor) {
        // 对容量值合法性的校验
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                    initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                    loadFactor);
        // 初始化负载因子和阈值
        // 注意:阈值在这里并非稳定值,在put方法进行初始化后,会等于数组长度*负载因子的值
        this.loadFactor = loadFactor;
        threshold = initialCapacity;
        // init是空实现,忽略
        init();
    }
           

接收容量的构造器:

// 接收容量的构造器,并将负载因子设置为默认的0.75
    public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }
           

最常用的构造器,无参构造:

// 用得最多的构造器,默认容量16、负载因子0.75
    public HashMap() {
        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
    }
           

接收一个map的构造器:

public HashMap(Map<? extends K, ? extends V> m) {
        this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
                DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
        inflateTable(threshold);

        putAllForCreate(m);
    }
           

2.3.3 get(Object key)方法的实现原理

// 取值
    public V get(Object key) {
        // 由于HashMap支持一个null值,因为key为null的时候,作特殊处理
        // null的值固定在table[0]下
        if (key == null)
            return getForNullKey();  // 代码1
        // 取entry
        Entry<K, V> entry = getEntry(key);  // 代码2

        return null == entry ? null : entry.getValue();
    }
           

由于HashMap支持key=null,而对于key=null的情况,由代码1进行特殊处理,下面是其对应逻辑:

// 取key=null的值
    private V getForNullKey() {
        if (size == 0) {
            return null;
        }
        for (Entry<K, V> e = table[0]; e != null; e = e.next) {
            if (e.key == null)
                return e.value;
        }
        return null;
    }
           

遍历table[0]下标下的值,由于key=null的特殊值,固定会放在table[0]下。假如table[0]下的结点,有key为null的,则将该值返回。

当key不为null时,由代码2取key对应的Entry,下面看看getEntry的实现逻辑:

// 获取键值对
    final Entry<K, V> getEntry(Object key) {
        if (size == 0) {
            return null;
        }

        // 计算key的hash值
        int hash = (key == null) ? 0 : hash(key);
        // 遍历某个下标下的链表(如果是链表的话)
        for (Entry<K, V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            // 如果key的hash相同 && (key相同或者key的equals方法的值相同)则返回
            if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                return e;
        }
        return null;
    }
           

该方法分两步,首先根据key,计算该key对应的下标值。

第二步,遍历下标对应的链表,然后分别判断其hash值是否相等且(key相同或者key的equals方法相等)则将该值返回。

2.3.4 put(K key, V value)方法的实现原理

// 添加值
    public V put(K key, V value) {
        // 如果table为空,则根据阈值初始化table的值。
        // 由于HashMap是延迟初始化的,构造器只给table赋值为EMPTY_TABLE
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);  // 代码3
        }
        // 特殊处理key=null的情况
        if (key == null)
            return putForNullKey(value);	// 代码4
        // 计算hash值
        int hash = hash(key);	// 代码5
        // 根据hash值与table长度,取模(实际为与操作)得到下标值
        int i = indexFor(hash, table.length);  // 代码6
        // 判断在当前下标下的链表有没有相同key值的Entry,有则覆盖,并将旧值返回
        for (Entry<K, V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        // 假如没有相同key值,则将为HashMap增加一个新值
        // 由于结构发生改变,因此modCount需要+1
        modCount++;
        // 添加新的Entry值
        addEntry(hash, key, value, i);  // 代码7
        return null;
    }
           

从上面构造器可以发现,HashMap是延迟初始化的,最开始的时候,给table赋值了一个EMPTY_TABLE,但真正将值设置进来的时候,才去申请capacity对应的空间。

代码3,当table==EMPTY_TABLE的时候,将执行初始化操作,下面代码看看其如何初始化的:

// 初始化table的值
    private void inflateTable(int toSize) {
        // 将容量控制为2的n次方
        int capacity = roundUpToPowerOf2(toSize);

        // 阈值载不大于MAXIMUM_CAPACITY+1的情况下,值等于capacity*loadFactor
        threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
        // 根据容量,创建Entry数组
        table = new Entry[capacity];
        // 初始化hash种子,可能会对hashSeed字段赋值,hashSeed可能会影响hash(Object key)方法的计算,下面会讲述
        initHashSeedAsNeeded(capacity);
    }
           

回到put方法的代码,在完成table值的初始化后,往下执行到代码4,会对key==null的情况进行特殊处理:

// 特殊处理key=null的值,将其放在table[0]下
    private V putForNullKey(V value) {
        for (Entry<K, V> e = table[0]; e != null; e = e.next) {
            if (e.key == null) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        modCount++;
        // 添加键值对
        addEntry(0, null, value, 0);
        return null;
    }
           

如果key!=null的时候,计算key对象的hash值,并取模得到key对应table的下标值,下面统一对于代码代码5和代码6进行分析:

代码5:

// 对key值作hash运算
    final int hash(Object k) {
        // hashSeed在大部分情况下是0,当容量超过2147483647后,可能会变为一个随机hashSeed(该情况不做分析)
        int h = hashSeed;
        if (0 != h && k instanceof String) {
            return sun.misc.Hashing.stringHash32((String) k);
        }

        // h为0的时候,下面异或操作结果将是 右边操作数的值,则下面相当于h = k.hashCode()
        h ^= k.hashCode();

        // 以下代码使用了扰动计算,异或了高位+低位的值,避免高位没有参与计算,导致在与table-1做与运算的时候,无法散列在各个下标值下
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }
           

在hashSeed不为0的时候且key为String类型的时候,可能会调用Hashing.stringHash32进行特殊处理,来的到hash值。

以上情况不满足的时候,将key的hashCode赋值给h,然后通过异或h的多个比特位进行计算。这里用了扰动计算,将hashCode的每个值都用到计算中,以支持散列分布。

代码6:

// 获取当前key下落在table哪个下标,用与运算,等效取模,但性能比取模快
    static int indexFor(int h, int length) {
        // 首先,length要求为2的n次方,2的n次方-1的时候,得到一个二进制是全1的值
        // 当h的值去与操作一个全1的值,等效于对 %length
        // 那为什么不用取模操作,而要与操作呢?因为与操作效率比取模操作速度快很多
        return h & (length - 1);
    }
           

回到put方法,在计算取得下标值后,将判断当前添加的键值对key是否已存在,开始遍历保存在该下标下的链表,当结点的hash值相等且(key相同或者key的equals方法相等)则覆盖该值,并将旧值返回。

如果以上情况都不满足,则说明当前添加的Entry既不是key==null,又不是key存在的情况。那么开始新增一个键值对,然后返回null。该操作将可能触发扩容。

代码7:

// 添加新的键值对
    void addEntry(int hash, K key, V value, int bucketIndex) {
        // 判断当前容器大小是否大于等于阈值 && 当前table下标的值存在值,将进行扩容操作
        if ((size >= threshold) && (null != table[bucketIndex])) {
            // 扩容为当前table长度的两倍
            resize(2 * table.length);  // 代码8 
            // 重新计算扩容后,当前key将位于table的哪个下标
            hash = (null != key) ? hash(key) : 0;
            bucketIndex = indexFor(hash, table.length);
        }
        // 创建键值对
        createEntry(hash, key, value, bucketIndex);
    }
           

代码8的扩容方法,在下面讲述,将解析其为什么在多线程环境下会形成环路。

2.3.5 resize()方法原理及多线程环境下环路的形成原因

// 扩容方法,多线程不安全,其他方法也是多线程不安全,但是resize问题比较严重
    // 可能形成环形链表,造成死循环,CPU100%
    void resize(int newCapacity) {
        // 暂存旧的table
        Entry[] oldTable = table;
        // 暂存旧table长度
        int oldCapacity = oldTable.length;
        // 假如旧的table长度等于最大容量,将不做扩容操作
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }

        // 根据新容量,新建新table
        Entry[] newTable = new Entry[newCapacity];
        
        // 执行table上面值得转移,将oldTable上面的值转移到newTable,会重新hash并取模来判断新的下标值
        // 多线程环境下,下面方法可能会产生环形链表,造成死循环
        transfer(newTable, initHashSeedAsNeeded(newCapacity));
        // 将newTable 替换为table
        table = newTable;
        // 重新计算阈值 ,控制阈值不能超过MAXIMUM_CAPACITY+1
        threshold = (int) Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
    }
           

在进行newCapacity(新容量)的合法值校验完成后,创建一个新的Entry数组,然后开始调用transfer进行元素的转移,意思就是把旧数组上面的元素值,进行重新hash并取模,确定其在新数组的下标,我们来看看其如何实现:

// 转移值
    void transfer(Entry[] newTable, boolean rehash) {
        // 获取新的容量
        int newCapacity = newTable.length;
        // 遍历table的Entry
        for (Entry<K, V> e : table) {
            // 遍历链表(如果是链表的话)
            while (null != e) {
                // 假如线程A取到next,然后时间片用完,CPU切换到线程B继续执行链表值转移操作,并执行完毕
                // 那么线程A有了CPU时间片后,继续执行,此时再触发转移值操作,很容易会形成环形链表,造成死循环
                Entry<K, V> next = e.next;
                if (rehash) {
                    e.hash = null == e.key ? 0 : hash(e.key);
                }
                // 根据hash值取模,得到在新table的下标值
                int i = indexFor(e.hash, newCapacity);
                
                // 采用头插法,将当前节点下一个节点指向newTable当前下标的值
                e.next = newTable[i];
                // 将当前节点作为newTable的头结点
                newTable[i] = e;
                // 将下一个节点赋值给下一轮循环的结点,继续往下执行循环
                e = next;
            }
        }
    }
           

这里注意一点是,newTable是作为方法参数传递进来,每个线程都会创建一个新的newTable,其值是线程私有,而线程不安全的变量,其实是table里面的Entry。具体如何成环路的场景分析,请参考下面的示例图。

JDK1.7版本HashMap的源码分析JDK1.7版本HashMap

图片地址: https://www.processon.com/diagraming/5e06f27ce4b0125e2924abba

2.3.5 keySet()方法的实现原理

public Set<K> keySet() {
        Set<K> ks = keySet;
        return (ks != null ? ks : (keySet = new KeySet()));
    }
           

上述代码为keySet实现原理,很简单,就是在keySet为null的时候,new了一个KeySet对象,那下面我们看看KeySet对象:

private final class KeySet extends AbstractSet<K> {
		// 实现Set的迭代方法
        public Iterator<K> iterator() {
            return newKeyIterator();
        }
        // 大小值跟HashMap大小一致
        public int size() {
            return size;
        }
        // 调用HashMap的containsKey方法
        public boolean contains(Object o) {
            return containsKey(o);
        }
        // 调用HashMap的removeEntryForKey方法
        public boolean remove(Object o) {
            return HashMap.this.removeEntryForKey(o) != null;
        }
        // 调用HashMap的clear方法
        public void clear() {
            HashMap.this.clear();
        }
    }
           

由上述代码看出,其具体实现,全都依赖于HashMap。这里可能有个困惑的点,就是我们看到一个Set对象,就会想当然觉得里面的元素都是存储在Set对象里面,其实不然。KeySet对象的值,如元素的值,集合大小等信息,其实都是用着HashMap的成员域。如果你获取到KeySet对象后,调用了remove方法,实际上会改变该key对应在HashMap中的Entry。

我们分析一下iterator方法:

public Iterator<K> iterator() {
			// 	调用迭代器方法时,会创建一个新的Key迭代器
            return newKeyIterator();
    }
           

调用HashMap的方法newKeyIterator创建一个KeyIterator对象。

Iterator<K> newKeyIterator()   {
		// 每次调用都会创建一个新的KeyIterator
        return new KeyIterator();
    }
           

KeyIterator 类:

private final class KeyIterator extends HashIterator<K> {
        public K next() {
            return nextEntry().getKey();
        }
    }
           

其next方法是调用父类HashIterator的nextEntry方法获取一个Entry,然后再获取Entry的key,这跟ValueIterator的实现是对应的。

下面我们看看nextEntry方法的具体实现:

private abstract class HashIterator<E> implements Iterator<E> {
        Entry<K,V> next;        // 需要返回的下一个Entry
        int expectedModCount;   // 快速失败的判断依据,如果该值不等于HashMap的modCount,将抛出ConcurrentModificationException
        int index;              // 当前迭代下标
        Entry<K,V> current;     // 当前Entry

        HashIterator() {
        	// 将HashMap结构修改次数保存一个副本,下面迭代的时候,如果两个值不相等,将抛出并发修改异常(其实不太准确,下面案例分析)ConcurrentModificationException
            expectedModCount = modCount;
            if (size > 0) { // advance to first entry
                Entry[] t = table;
                // 遍历table,直到定位到table不为null的值
                while (index < t.length && (next = t[index++]) == null);
            }
        }

        public final boolean hasNext() {
            return next != null;
        }

        final Entry<K,V> nextEntry() {
        	// 判断在执行迭代器期间,HashMap有没有对结构进行修改
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            Entry<K,V> e = next;
            if (e == null)
                throw new NoSuchElementException();

            if ((next = e.next) == null) {
                Entry[] t = table;
                while (index < t.length && (next = t[index++]) == null)
                    ;
            }
            current = e;
            return e;
        }

        public void remove() {
            if (current == null)
                throw new IllegalStateException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            Object k = current.key;
            current = null;
            HashMap.this.removeEntryForKey(k);
            expectedModCount = modCount;
        }
    }
           

从HashIterator的代码我们可以看出,在其成员域中维护了

3 源码

下面是源码,篇幅较长,上文已将重要方法都讲解了,如果没有兴趣,可以不看。篇幅长,所以可能会比较卡!

package hashmap1_7;

import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.Serializable;
import java.util.*;

/**
 * JDK1.7版本,加入个人一些理解、注释
 * 当使用该类的时候,记得要把项目的JDK版本切换为1.7,否则会报错
 * copy form jdk1.7
 * @author wzj
 * 2019/12/28
 */
public class HashMap<K, V>
        extends AbstractMap<K, V>
        implements Map<K, V>, Cloneable, Serializable {

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

    /**
     * 最大容量值
     */
    static final int MAXIMUM_CAPACITY = 1 << 30;

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

    /**
     * 空table
     */
    static final Entry<?, ?>[] EMPTY_TABLE = {};

    /**
     * table,用来存放键值对,键值对可以单个值,也可以是一个链表
     */
    transient Entry<K, V>[] table = (Entry<K, V>[]) EMPTY_TABLE;

    /**
     * 大小
     */
    transient int size;

    /**
     * 阈值,扩容的主要判断条件之一
     */
    int threshold;

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

    /**
     * 结构更改次数的统计,增、删都会加1。也是快速失败的触发依据,后面HashIterator会介绍如何使用该值
     */
    transient int modCount;

    /**
     * 默认备选hash阈值
     */
    static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE;

    /**
     * 注意、注意、注意:
     * 这两个成员变量来自父类AbstractMap,由于其为default类型,在不同包将访问不到,因此在此声明,以此代替
     * 由于keySet、value在父类中的keySet()、values()、clone()方法中用到,而HashMap这几个方法全部重写了,因此不会影响使用
     */
    transient volatile Set<K> keySet = null;
    transient volatile Collection<V> values = null;

    private static class Holder {

        /**
         * 备选hash阈值
         */
        static final int ALTERNATIVE_HASHING_THRESHOLD;

        static {
            // 从系统获取阈值的配置,一般没有配置
            String altThreshold = java.security.AccessController.doPrivileged(
                    new sun.security.action.GetPropertyAction(
                            "jdk.map.althashing.threshold"));

            int threshold;
            try {
                // 给阈值赋值
                threshold = (null != altThreshold)
                        ? Integer.parseInt(altThreshold)
                        : ALTERNATIVE_HASHING_THRESHOLD_DEFAULT;

                // 对阈值合法性进行校验
                if (threshold == -1) {
                    threshold = Integer.MAX_VALUE;
                }

                if (threshold < 0) {
                    throw new IllegalArgumentException("value must be positive integer.");
                }
            } catch (IllegalArgumentException failed) {
                throw new Error("Illegal value for 'jdk.map.althashing.threshold'", failed);
            }

            // 最终给备选阈值设置值
            ALTERNATIVE_HASHING_THRESHOLD = threshold;
        }
    }

    /**
     * hash种子
     */
    transient int hashSeed = 0;

    public HashMap(int initialCapacity, float loadFactor) {
        // 对容量值合法性的校验
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                    initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                    loadFactor);
        // 初始化负载因子和阈值
        // 注意:阈值在这里并非稳定值,在put方法进行初始化后,会等于数组长度*负载因子的值
        this.loadFactor = loadFactor;
        threshold = initialCapacity;
        // init是空实现,忽略
        init();
    }

    // 接收容量的构造器,并将负载因子设置为默认的0.75
    public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }

    // 用得最多的构造器,默认容量16、负载因子0.75
    public HashMap() {
        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
    }

    // 接收一个map的构造器
    public HashMap(Map<? extends K, ? extends V> m) {
        this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
                DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
        inflateTable(threshold);

        putAllForCreate(m);
    }

    // 根据传入的值,得到一个接近的2的n次方数
    private static int roundUpToPowerOf2(int number) {
        // 以下操作分三步
        // 1、判断是否超过容量最大值,超过则取容量最大值
        // 2、判断是否小于最小值,小于则取最小值1
        // 3、当上述情况不符合,则说明值在正常范围
        // 让该值-1再乘以2,然后调用Integer的highestOneBit方法,向下取一个最近的2的n次方值
        // 如:1取1(2^0)、 2-3取2(2^1)、 4-7取4(2^2) 、8-15取8(2^3) 、16-31取16(2^4)
        return number >= MAXIMUM_CAPACITY
                ? MAXIMUM_CAPACITY
                : (number > 1) ? Integer.highestOneBit((number - 1) << 1) : 1;
    }

    // 初始化table的值
    private void inflateTable(int toSize) {
        // 将容量控制为2的n次方
        int capacity = roundUpToPowerOf2(toSize);

        // 阈值载不大于MAXIMUM_CAPACITY+1的情况下,值等于capacity*loadFactor
        threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
        // 根据容量,创建Entry数组
        table = new Entry[capacity];
        // 初始化hash种子,可能会对hashSeed字段赋值,hashSeed可能会影响hash(Object key)方法的计算,下面会讲述
        initHashSeedAsNeeded(capacity);
    }

    void init() {
    }

    // 初始化哈希掩码值。我们将初始化推迟到真正需要的时候。
    final boolean initHashSeedAsNeeded(int capacity) {
        // 第一次进入该方法,currentAltHashing一直为false
        boolean currentAltHashing = hashSeed != 0;
        // 只有等到容量值超过2147483647,则会触发给hashSeed赋值
        // sun.misc.VM.isBooted为虚拟机启动后一直为true && 容量大于最大值
        boolean useAltHashing = sun.misc.VM.isBooted() &&
                (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
        boolean switching = currentAltHashing ^ useAltHashing;

        if (switching) {
            // 假如useAltHashing为true,则将hashSeed设置为随机产生的HashSeed
            hashSeed = useAltHashing
                    ? sun.misc.Hashing.randomHashSeed(this)
                    : 0;
        }
        return switching;
    }

    // 对key值作hash运算
    final int hash(Object k) {
        // hashSeed在大部分情况下是0,当容量超过2147483647后,可能会变为一个随机hashSeed(该情况不做分析)
        int h = hashSeed;
        if (0 != h && k instanceof String) {
            return sun.misc.Hashing.stringHash32((String) k);
        }

        // h为0的时候,下面异或操作结果将是 右边操作数的值,则下面相当于h = k.hashCode()
        h ^= k.hashCode();

        // 以下代码使用了扰动计算,异或了高位+低位的值,避免高位没有参与计算,导致在与table-1做与运算的时候,无法散列在各个下标值下
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }


    // 获取当前key下落在table哪个下标,用与运算,等效取模,但性能比取模快
    static int indexFor(int h, int length) {
        // 首先,length要求为2的n次方,2的n次方-1的时候,得到一个二进制是全1的值
        // 当h的值去与操作一个全1的值,等效于对 %length
        // 那为什么不用取模操作,而要与操作呢?因为与操作效率比取模操作速度快很多
        return h & (length - 1);
    }

    public int size() {
        return size;
    }

    public boolean isEmpty() {
        return size == 0;
    }

    // 取值
    public V get(Object key) {
        // 由于HashMap支持一个null值,因为key为null的时候,作特殊处理
        // null的值固定在table[0]下
        if (key == null)
            return getForNullKey();
        // 取entry
        Entry<K, V> entry = getEntry(key);

        return null == entry ? null : entry.getValue();
    }

    // 取key=null的值
    private V getForNullKey() {
        if (size == 0) {
            return null;
        }
        for (Entry<K, V> e = table[0]; e != null; e = e.next) {
            if (e.key == null)
                return e.value;
        }
        return null;
    }

    public boolean containsKey(Object key) {
        return getEntry(key) != null;
    }

    // 获取键值对
    final Entry<K, V> getEntry(Object key) {
        if (size == 0) {
            return null;
        }

        // 计算key的hash值
        int hash = (key == null) ? 0 : hash(key);
        // 遍历某个下标下的链表(如果是链表的话)
        for (Entry<K, V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            // 如果key相同且值相同,则返回
            if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                return e;
        }
        return null;
    }

    // 添加值
    public V put(K key, V value) {
        // 如果table为空,则根据阈值初始化table的值。
        // 由于HashMap是延迟初始化的,构造器只给table赋值为EMPTY_TABLE
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        // 特殊处理key=null的情况
        if (key == null)
            return putForNullKey(value);
        // 计算hash值
        int hash = hash(key);
        // 根据hash值与table长度,取模(实际为与操作)得到下标值
        int i = indexFor(hash, table.length);
        // 判断在当前下标下的链表有没有相同key值的Entry,有则覆盖,并将旧值返回
        for (Entry<K, V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        // 假如没有相同key值,则将为HashMap增加一个新值
        // 由于结构发生改变,因此modCount需要+1
        modCount++;
        // 添加新的Entry值
        addEntry(hash, key, value, i);
        return null;
    }

    // 特殊处理key=null的值,将其放在table[0]下
    private V putForNullKey(V value) {
        for (Entry<K, V> e = table[0]; e != null; e = e.next) {
            if (e.key == null) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        modCount++;
        addEntry(0, null, value, 0);
        return null;
    }

    private void putForCreate(K key, V value) {
        int hash = null == key ? 0 : hash(key);
        int i = indexFor(hash, table.length);

        for (Entry<K, V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k)))) {
                e.value = value;
                return;
            }
        }

        createEntry(hash, key, value, i);
    }

    private void putAllForCreate(Map<? extends K, ? extends V> m) {
        for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
            putForCreate(e.getKey(), e.getValue());
    }

    // 扩容方法,多线程不安全,其他方法也是多线程不安全,但是resize问题比较严重
    // 可能形成环形链表,造成死循环,CPU100%
    void resize(int newCapacity) {
        // 暂存旧的table
        Entry[] oldTable = table;
        // 暂存旧table长度
        int oldCapacity = oldTable.length;
        // 假如旧的table长度等于最大容量,将不做扩容操作
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }

        // 根据新容量,新建新table
        Entry[] newTable = new Entry[newCapacity];
        // 执行table上面值得转移,将oldTable上面的值转移到newTable,会重新hash并取模来判断新的下标值
        // 多线程环境下,下面方法可能会产生环形链表,造成死循环
        transfer(newTable, initHashSeedAsNeeded(newCapacity));
        // 将newTable 替换为table
        table = newTable;
        // 重新计算阈值 ,控制阈值不能超过MAXIMUM_CAPACITY+1
        threshold = (int) Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
    }

    // 转移值
    void transfer(Entry[] newTable, boolean rehash) {
        // 获取新的容量
        int newCapacity = newTable.length;
        // 遍历table的Entry
        for (Entry<K, V> e : table) {
            // 遍历链表(如果是链表的话)
            while (null != e) {
                // 假如线程A取到next,然后时间片用完,CPU切换到线程B继续执行链表值转移操作,并执行完毕
                // 那么线程A有了CPU时间片后,继续执行,此时再触发转移值操作,很容易会形成环形链表,造成死循环
                Entry<K, V> next = e.next;

                if (rehash) {
                    e.hash = null == e.key ? 0 : hash(e.key);
                }
                // 根据hash值取模,得到在新table的下标值
                int i = indexFor(e.hash, newCapacity);
                // 采用头插法,将当前节点下一个节点指向newTable当前下标的值
                e.next = newTable[i];
                // 将当前节点作为newTable的头结点
                newTable[i] = e;
                // 将下一个节点赋值给下一轮循环的结点,继续往下执行循环
                e = next;
            }
        }
    }

    public void putAll(Map<? extends K, ? extends V> m) {
        int numKeysToBeAdded = m.size();
        if (numKeysToBeAdded == 0)
            return;

        if (table == EMPTY_TABLE) {
            inflateTable((int) Math.max(numKeysToBeAdded * loadFactor, threshold));
        }

        if (numKeysToBeAdded > threshold) {
            int targetCapacity = (int) (numKeysToBeAdded / loadFactor + 1);
            if (targetCapacity > MAXIMUM_CAPACITY)
                targetCapacity = MAXIMUM_CAPACITY;
            int newCapacity = table.length;
            while (newCapacity < targetCapacity)
                newCapacity <<= 1;
            if (newCapacity > table.length)
                resize(newCapacity);
        }

        for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
            put(e.getKey(), e.getValue());
    }

    // 移除key方法
    public V remove(Object key) {
        // 移除key对应的Entry
        Entry<K, V> e = removeEntryForKey(key);
        return (e == null ? null : e.value);
    }

    // 移除key对应的Entry
    final Entry<K, V> removeEntryForKey(Object key) {
        if (size == 0) {
            return null;
        }
        // 根据hash值和table长度取模,计算当前key所在table的下标
        int hash = (key == null) ? 0 : hash(key);
        int i = indexFor(hash, table.length);
        // 获取下标所在头结点
        Entry<K, V> prev = table[i];
        Entry<K, V> e = prev;

        // 遍历链表
        while (e != null) {
            Entry<K, V> next = e.next;
            Object k;
            // 假如hash值相等 && key的equals方法也相同,则将执行移除
            if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k)))) {
                // 结构变化自增
                modCount++;
                // 大小减1
                size--;
                // 判断当前key是否为头结点,如果是,需要将next做为新的头结点,放在table[i]下
                // 为什么说prev==e就是key为头结点呢,因为prev==e只有在第一次判断的时候才会相等,下面的prev=e和e=next会改变其值了
                if (prev == e)
                    table[i] = next;
                    // 当前key对应的值非头结点,则将当前结点的next赋值给上个结点的next,因为准备删除当前结点
                else
                    prev.next = next;
                // 空方法,忽略
                e.recordRemoval(this);
                return e;
            }
            // 给下一轮循环的结点赋值,向下遍历结点
            prev = e;
            e = next;
        }

        return e;
    }

    final Entry<K, V> removeMapping(Object o) {
        if (size == 0 || !(o instanceof Map.Entry))
            return null;

        Map.Entry<K, V> entry = (Map.Entry<K, V>) o;
        Object key = entry.getKey();
        int hash = (key == null) ? 0 : hash(key);
        int i = indexFor(hash, table.length);
        Entry<K, V> prev = table[i];
        Entry<K, V> e = prev;

        while (e != null) {
            Entry<K, V> next = e.next;
            if (e.hash == hash && e.equals(entry)) {
                modCount++;
                size--;
                if (prev == e)
                    table[i] = next;
                else
                    prev.next = next;
                e.recordRemoval(this);
                return e;
            }
            prev = e;
            e = next;
        }

        return e;
    }

    public void clear() {
        modCount++;
        Arrays.fill(table, null);
        size = 0;
    }

    public boolean containsValue(Object value) {
        if (value == null)
            return containsNullValue();

        Entry[] tab = table;
        for (int i = 0; i < tab.length; i++)
            for (Entry e = tab[i]; e != null; e = e.next)
                if (value.equals(e.value))
                    return true;
        return false;
    }

    private boolean containsNullValue() {
        Entry[] tab = table;
        for (int i = 0; i < tab.length; i++)
            for (Entry e = tab[i]; e != null; e = e.next)
                if (e.value == null)
                    return true;
        return false;
    }

    public Object clone() {
        HashMap<K, V> result = null;
        try {
            result = (HashMap<K, V>) super.clone();
        } catch (CloneNotSupportedException e) {
            // assert false;
        }
        if (result.table != EMPTY_TABLE) {
            result.inflateTable(Math.min(
                    (int) Math.min(
                            size * Math.min(1 / loadFactor, 4.0f),
                            // we have limits...
                            HashMap.MAXIMUM_CAPACITY),
                    table.length));
        }
        result.entrySet = null;
        result.modCount = 0;
        result.size = 0;
        result.init();
        result.putAllForCreate(this);

        return result;
    }

    // 键值对,其在维护key和value的同时,还会维护next(下一个Entry)和hash(key的hash值)
    static class Entry<K, V> implements Map.Entry<K, V> {
        final K key;
        V value;
        Entry<K, V> next;
        int hash;

        Entry(int h, K k, V v, Entry<K, V> n) {
            value = v;
            next = n;
            key = k;
            hash = h;
        }

        public final K getKey() {
            return key;
        }

        public final V getValue() {
            return value;
        }

        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }

        public final boolean equals(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry e = (Map.Entry) o;
            Object k1 = getKey();
            Object k2 = e.getKey();
            if (k1 == k2 || (k1 != null && k1.equals(k2))) {
                Object v1 = getValue();
                Object v2 = e.getValue();
                if (v1 == v2 || (v1 != null && v1.equals(v2)))
                    return true;
            }
            return false;
        }

        public final int hashCode() {
            return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue());
        }

        public final String toString() {
            return getKey() + "=" + getValue();
        }

        void recordAccess(HashMap<K, V> m) {
        }

        void recordRemoval(HashMap<K, V> m) {
        }
    }

    // 添加新的键值对
    void addEntry(int hash, K key, V value, int bucketIndex) {
        // 判断当前容器大小是否大于等于阈值 && 当前table下标的值存在值,将进行扩容操作
        if ((size >= threshold) && (null != table[bucketIndex])) {
            // 扩容为当前table长度的两倍
            resize(2 * table.length);
            // 重新计算扩容后,当前key将位于table的哪个下标
            hash = (null != key) ? hash(key) : 0;
            bucketIndex = indexFor(hash, table.length);
        }
        // 创建键值对
        createEntry(hash, key, value, bucketIndex);
    }
    // 创建键值对
    void createEntry(int hash, K key, V value, int bucketIndex) {
        // 获取指定下标的元素,作为新结点的next,然后将新结点作为table[index]的头结点
        Entry<K, V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<>(hash, key, value, e);
        // 大小+1
        size++;
    }
    // Hash迭代器,KeyIterator和ValueIterator的父类
    private abstract class HashIterator<E> implements Iterator<E> {
        Entry<K, V> next;        // 需要返回的下一个Entry
        int expectedModCount;    // 快速失败的判断依据,如果该值不等于HashMap的modCount,将抛出ConcurrentModificationException
        int index;               // 当前迭代下标
        Entry<K, V> current;     // 当前Entry

        HashIterator() {
            // 将HashMap结构修改次数保存一个副本,下面迭代的时候,如果两个值不相等
            // 将抛出并发修改异常(其实不太准确,下面案例分析)ConcurrentModificationException
            expectedModCount = modCount;
            if (size > 0) {
                // 遍历table,直到定位到table不为null的值
                Entry[] t = table;
                while (index < t.length && (next = t[index++]) == null) ;
            }
        }

        public final boolean hasNext() {
            return next != null;
        }

        final Entry<K, V> nextEntry() {
            // 判断在执行迭代器期间,HashMap有没有对结构进行修改
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            Entry<K, V> e = next;
            if (e == null)
                throw new NoSuchElementException();

            if ((next = e.next) == null) {
                Entry[] t = table;
                while (index < t.length && (next = t[index++]) == null)
                    ;
            }
            current = e;
            return e;
        }

        public void remove() {
            if (current == null)
                throw new IllegalStateException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            Object k = current.key;
            current = null;
            HashMap.this.removeEntryForKey(k);
            expectedModCount = modCount;
        }
    }

    private final class ValueIterator extends HashIterator<V> {
        public V next() {
            return nextEntry().value;
        }
    }

    private final class KeyIterator extends HashIterator<K> {
        public K next() {
            return nextEntry().getKey();
        }
    }

    private final class EntryIterator extends HashIterator<Map.Entry<K, V>> {
        public Map.Entry<K, V> next() {
            return nextEntry();
        }
    }

    Iterator<K> newKeyIterator() {
        return new KeyIterator();
    }

    Iterator<V> newValueIterator() {
        return new ValueIterator();
    }

    Iterator<Map.Entry<K, V>> newEntryIterator() {
        return new EntryIterator();
    }

    // 键值对集合
    private transient Set<Map.Entry<K, V>> entrySet = null;

    // 获取key集合
    public Set<K> keySet() {
        Set<K> ks = keySet;
        return (ks != null ? ks : (keySet = new KeySet()));
    }
    // key集合类,特点:其成员本身不保存元素值,而是在每次迭代的时候,去取HashMap的table上的值
    private final class KeySet extends AbstractSet<K> {
        public Iterator<K> iterator() {
            return newKeyIterator();
        }

        public int size() {
            return size;
        }

        public boolean contains(Object o) {
            return containsKey(o);
        }

        public boolean remove(Object o) {
            return HashMap.this.removeEntryForKey(o) != null;
        }

        public void clear() {
            HashMap.this.clear();
        }
    }

    // 获取值得集合
    public Collection<V> values() {
        Collection<V> vs = values;
        return (vs != null ? vs : (values = new Values()));
    }

    // value集合类,特点:其成员本身不保存元素值,而是在每次迭代的时候,去取HashMap的table上的值
    private final class Values extends AbstractCollection<V> {
        public Iterator<V> iterator() {
            return newValueIterator();
        }

        public int size() {
            return size;
        }

        public boolean contains(Object o) {
            return containsValue(o);
        }

        public void clear() {
            HashMap.this.clear();
        }
    }

    public Set<Map.Entry<K, V>> entrySet() {
        return entrySet0();
    }

    private Set<Map.Entry<K, V>> entrySet0() {
        Set<Map.Entry<K, V>> es = entrySet;
        return es != null ? es : (entrySet = new EntrySet());
    }

    // 键值对集合类
    private final class EntrySet extends AbstractSet<Map.Entry<K, V>> {
        public Iterator<Map.Entry<K, V>> iterator() {
            return newEntryIterator();
        }

        public boolean contains(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<K, V> e = (Map.Entry<K, V>) o;
            Entry<K, V> candidate = getEntry(e.getKey());
            return candidate != null && candidate.equals(e);
        }

        public boolean remove(Object o) {
            return removeMapping(o) != null;
        }

        public int size() {
            return size;
        }

        public void clear() {
            HashMap.this.clear();
        }
    }

    // 序列化
    private void writeObject(java.io.ObjectOutputStream s)
            throws IOException {
        // Write out the threshold, loadfactor, and any hidden stuff
        s.defaultWriteObject();

        // Write out number of buckets
        if (table == EMPTY_TABLE) {
            s.writeInt(roundUpToPowerOf2(threshold));
        } else {
            s.writeInt(table.length);
        }

        // Write out size (number of Mappings)
        s.writeInt(size);

        // Write out keys and values (alternating)
        if (size > 0) {
            for (Map.Entry<K, V> e : entrySet0()) {
                s.writeObject(e.getKey());
                s.writeObject(e.getValue());
            }
        }
    }

    private static final long serialVersionUID = 362498820763181265L;

    // 反序列化
    private void readObject(java.io.ObjectInputStream s)
            throws IOException, ClassNotFoundException {
        // Read in the threshold (ignored), loadfactor, and any hidden stuff
        s.defaultReadObject();
        if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
            throw new InvalidObjectException("Illegal load factor: " +
                    loadFactor);
        }

        // set other fields that need values
        table = (Entry<K, V>[]) EMPTY_TABLE;

        // Read in number of buckets
        s.readInt(); // ignored.

        // Read number of mappings
        int mappings = s.readInt();
        if (mappings < 0)
            throw new InvalidObjectException("Illegal mappings count: " +
                    mappings);

        // capacity chosen by number of mappings and desired load (if >= 0.25)
        int capacity = (int) Math.min(
                mappings * Math.min(1 / loadFactor, 4.0f),
                // we have limits...
                HashMap.MAXIMUM_CAPACITY);

        // allocate the bucket array;
        if (mappings > 0) {
            inflateTable(capacity);
        } else {
            threshold = capacity;
        }

        init();  // Give subclass a chance to do its thing.

        // Read the keys and values, and put the mappings in the hashmap1_7.HashMap
        for (int i = 0; i < mappings; i++) {
            K key = (K) s.readObject();
            V value = (V) s.readObject();
            putForCreate(key, value);
        }
    }

    // 返回容量
    int capacity() {
        return table.length;
    }

    // 返回负载因子
    float loadFactor() {
        return loadFactor;
    }
}

           

https://gitee.com/lao-dubbo/source-code