Linux ·

HashMap 学习

部分源码解析

变量和常量

// 默认的初始化容量,十进制数为 16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

// 极限容量,也就是说 table 数组再大也不能超过这个容量
static final int MAXIMUM_CAPACITY = 1 << 30;

// 默认的负载因子:0.75(关系到 HashMap 本身的性能)
static final float DEFAULT_LOAD_FACTOR = 0.75f;

// 一个空的 Entry 数组,在 table 数组没有进行初始化扩容之前一直就是这个空数组
static final Entry<?,?>[] EMPTY_TABLE = {};

// HashMap 的存储结构就是这个 table 数组,长度为 2 的 n 次方数;
// 数组中存储的元素是一个个 Entry 对象,而 Entry 对象又实现了链表结构;
// 所以说 HashMap 的数据结构是 数组 + 链表,具体请看【存储结构】章节
transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;

// HashMap 存储的元素多少
transient int size;

// 阀值,一般来说为 实际容量 * 负载因子
int threshold;

// 实际的负载因子
final float loadFactor;

// 修改次数,用于快速失败,具体请看【快速失败】章节
transient int modCount;

构造方法

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);

    this.loadFactor = loadFactor;
    threshold = initialCapacity;
    init();// 此方法是空的
}

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

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

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);
}

(1). 前三个构造方法,无非就是设置一下初始化容量和负载因子,不多说。

(2). 而第四个构造方法:

  • 设置初始化容量和负载因子;
  • inflateTable(...) 初始化数组并扩充容量;
  • 将 map 对象塞进空的 HashMap 中。

inflateTable(...) 

// 对 table 数组进行扩容
private void inflateTable(int toSize) {
    // Find a power of 2 >= toSize
    int capacity = roundUpToPowerOf2(toSize);

    threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
    table = new Entry[capacity];
    initHashSeedAsNeeded(capacity);
}

private static int roundUpToPowerOf2(int number) {
    // assert number >= 0 : "number must be non-negative";
    return number >= MAXIMUM_CAPACITY
            ? MAXIMUM_CAPACITY
            : (number > 1) ? Integer.highestOneBit((number - 1) << 1) : 1;
}

(1). 一直往前推,很容易得知传进来的 toSize 参数就是初始化容量;

(2). roundUpToPowerOf2(toSize) 方法得到返回值是一个比 toSize 大的最小的 2 的 n 次方数 capacity,capacity 就是实际容量;

  • 若 toSize 为 15,则 capacity 为 16;若 toSize 为 17,则 capacity 为 32。因此可以看出:

参与评论