从源码,看一看HashMap,传递了初始化容量,初始化的数组长度是多少?

1,115 阅读2分钟

调用初始化容量的构造函数,初始数组长度是多少

从其他地方读到HashMap是利用数组+链表的方式存储元素,默认数组长度为16,负载因子为0.75,一直也认为数组伴随着HashMap创建,一开始就初始化,并且长度也就是16,最近在被问到题目这个问题时,若是初始化容量为30,也就是调用new HashMap(30);,初始数组的长度是多少?突然懵了,不是很确定这个长度会是30,还是30*0.75呢?或者是其它值???

话不多说,源码撸起来

构造函数

//只传递初始化容量,负载因子还是默认的0.75
public HashMap(int initialCapacity) {
    this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
public HashMap(int initialCapacity, float loadFactor) {
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal initial capacity: " +
                                           initialCapacity);
    //如果初始化的容量大于最大容量,就将初始容量设置为最大允许容量 MAXIMUM_CAPACITY = 1 << 30
    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();
}

从这个实际调用的构造函数来看,这里只是设置了属性 负载因子阈值,并未真正的创建数组,那默认的数组长度是多少呢?

/**
 * An empty table instance to share when the table is not inflated.
 */
static final Entry<?,?>[] EMPTY_TABLE = {};

/**
 * The table, resized as necessary. Length MUST Always be a power of two.
 */
transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;

可以看到,默认的table数组是一个空数组,也就是只调用了构造函数后,实际上并没有创建数组!!!

从源码来看,table数组赋值的地方有4处。

  1. 属性默认值,空数组
/**
 * The table, resized as necessary. Length MUST Always be a power of two.
 */
transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;
  1. 填充数组方法
/**
 * Inflates the 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);
}

从这可以看到,新建数组的长度为2的次方。 inflateTable方法有以下几处调用

调用方法
从put方法来看,toSize也就是threshold,而构造方法中,threshold=initialCapacity。
put方法
当initialCapacity=30时,capacity = 32
roundUpToPowerOf2
3. 扩容中,赋值新的数组

void resize(int newCapacity) {
    Entry[] oldTable = table;
    int oldCapacity = oldTable.length;
    if (oldCapacity == MAXIMUM_CAPACITY) {
        threshold = Integer.MAX_VALUE;
        return;
    }

    Entry[] newTable = new Entry[newCapacity];
    transfer(newTable, initHashSeedAsNeeded(newCapacity));
    table = newTable;
    threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
}
  1. 反序列化,设置为空数组
    private void readObject(java.io.ObjectInputStream s)
         throws IOException, ClassNotFoundException
    {
        ...

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

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

        ...
    }

从以上分析来看,当新建一个HashMap,若是并未使用,其实并没有初始化数组,若是调用put方法,懒初始化,其数组长度为32(2的5次方)。