jdk1.7HashMap

JDK7 HashMap 源码分析

public class HashMap<K,V>
    extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable

1、实现的接口

从文档中可以看出 HashMap 实现了 Serializable、Cloneable、Map<K,V>三个接口

Serializable:序列化接口

Cloneable:克隆接口

Map<K,V>:存储键值对的容器接口

2、子类

直接子类: LinkedHashMap、PrinterStateReasons

3、继承的类

AbstractMap

4、源码分析

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

默认初始容量为 1 < < 4 1 << 41<<4 即 16

static final int MAXIMUM_CAPACITY = 1 << 30;

最大容量为 1 < < 30 1 << 301<<30 即 1073741824

static final float DEFAULT_LOAD_FACTOR = 0.75f;

加载因子为 0.75

static final Entry<?,?>[] EMPTY_TABLE = {};

一个不能修改引用的 Entry 空键值对数组

transient Entry<?,?>[] table = EMPTY_TABLE;

transient 关键字,防止在序列化时序列化不必要的属性

把空数组的引用赋值给 table

transient int size;

键值对映射的数量

int threshold;

阈值:到达这个值后就进行扩容

阈 值 = 容 量 ∗ 加 载 因 子 阈值 = 容量 * 加载因子=

final float loadFactor;

最终的加载因子

transient int modCount;

HashMap 修改的次数

static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE;

默认的最大阈值 int 类型的最大值

transient int hashSeed = 0;

hash种子初始化为 0

public HashMap(int initialCapacity, float loadFactor) {
    // 如果容量小于 0 抛出异常
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
    // 如果指定的容量 > 最大容量,那指定容量就是最大容量
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
    // 如果加载因子 <= 0 或 不是 flaot 类型就抛出异常
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);

    // 加载因子初始化
        this.loadFactor = loadFactor;
    // 阈值 = 指定容量
        threshold = initialCapacity;
        init();
    }

HashMap 的有参构造函数,initialCapacity 指定容量,loadFactor 指定加载因子

void init() {
    }

init 方法为空函数,没有方法体

public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }

只指定容量,使用默认的加载因子,调用双参构造函数

public HashMap() {
        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
    }

无参构造函数,使用默认容量(16)和默认的加载因子(0.75)

void resize(int newCapacity) {
    // 将 table 用 oldTable 存储
        Entry<?,?>[] oldTable = table;
    // 旧容量为 oldCapacity
        int oldCapacity = oldTable.length;
    // 如果旧容量为最大容量, 阈值就为 int 的最大值 7fffffff
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }
	// 定义新的 Entry 数组 newTable
        Entry<?,?>[] newTable = new Entry<?,?>[newCapacity];
    // 
        transfer(newTable, initHashSeedAsNeeded(newCapacity));
    // 将新的 NewTable 赋值给 table
        table = newTable;
    // 计算新的阈值
    // 阈值 = min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1)
        threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
    }
// 初始化 Hash 种子
final boolean initHashSeedAsNeeded(int capacity) {
    // 先判断当前的 hashSeed 是否等于 0,用 currentAltHashing 保存
        boolean currentAltHashing = hashSeed != 0;
        boolean useAltHashing = sun.misc.VM.isBooted() &&
                (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
        boolean switching = currentAltHashing ^ useAltHashing;
        if (switching) {
            hashSeed = useAltHashing
                ? sun.misc.Hashing.randomHashSeed(this)
                : 0;
        }
        return switching;
    }
// 新的 table 表,是否有 hash 种子
void transfer(Entry<?,?>[] newTable, boolean rehash) {
    // 将 table 定义为原Entry数组
        Entry<?,?>[] src = table;
    // newTable 的长度为 newCapacity
        int newCapacity = newTable.length;
    // 遍历原Entry数组
        for (int j = 0; j < src.length; j++) {
            // 将原数组的值对应下标放到新的数组中
            Entry<K,V> e = (Entry<K,V>)src[j];
            // 只要 e 不为 null 就一直循环
            while(null != e) {
                // 将 e 的下一个取出来,用 next 保存
                Entry<K,V> next = e.next;
                // 如果有 hash 种子
                if (rehash) {
                    // 判断 e.key(e的键) 是否为 null 是就为 0,否就为e.key 的 hash 值
                    e.hash = null == e.key ? 0 : hash(e.key);
                }
                // 返回 e.hash 的索引位置
                int i = indexFor(e.hash, newCapacity);
                // 将Entry数组的第 i 个元素放到 e.next 形成链表
                e.next = (Entry<K,V>)newTable[i];
                // 将 e 放到 i 的位置
                newTable[i] = e;
                // 将下一个赋给 e 进行循环
                e = next;
            }
        }
    }
final int hash(Object k) {
    // hash 种子用 h 来保存
        int h = hashSeed;
    // 如果 h 不为 0 且 k 是 String 的实例
        if (0 != h && k instanceof String) {
            // 返回 32 位的 hash 值
            return sun.misc.Hashing.stringHash32((String) k);
        }
	// h = h ^ k.hashCode();
        h ^= k.hashCode();

        // This function ensures that hashCodes that differ only by
        // constant multiples at each bit position have a bounded
        // number of collisions (approximately 8 at default load factor).
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }
static int indexFor(int h, int length) {
        // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";
    // 返回 h % length
        return h & (length-1);
    }
void addEntry(int hash, K key, V value, int bucketIndex) {
    if ((size >= threshold) && (null != table[bucketIndex])) {
        // 扩容为当前 table 长度的两倍
        resize(2 * table.length);
        hash = (null != key) ? hash(key) : 0;
        bucketIndex = indexFor(hash, table.length);
    }

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

总结:从HashMap的底层源码中可以看出

数据结构为:数组 + 单向链表 (使用的前插法)

默认初始容量为 16,默认加载因子为 0.75

从 addEntry 方法可以看出扩容为:原数组长度 * 2

没有 synchronized 关键字修饰:线程不安全

在扩容时二次哈希,重新获取位置


版权声明:本文为m0_51900835原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。