[javascript学习指南]Java HashMap初始容量的取值示例

更新时间:2019-10-17    来源:js教程    手机版     字体:

【www.bbyears.com--js教程】


HashMap中底层数据的长度总是2的n次方

在某个元素存入HashMap底层数组时,为确定其位置,最直接的方式是对其取模,这样能够均匀的分布到数组中。这里比较取巧的是,当数组长为2的n次方时,通过h&(length-1)能够高效的算出hash值。

/**
 * Returns index for hash code h.
 */
static int indexFor(int h, int length) {
    // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";
    return h & (length-1);
}
HashMap默认初始容量为16,负载因子loadFactor为0.75,也就是说只能存储12个元素,当put第13个元素时就需要resize数组将容量扩充到32。

确定初始容量

最初一直通过手动计算算出初始容量,算出大于数据size的最小的2的n次方,当然也要考虑加载因子。这样每次计算都很麻烦,将负载因子设为1可以简单计算。但查看源码,HashMap做了Size计算。

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();
}
/**
 * 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);
}
 
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;
}

可以看出我们定义的initialCapacity先赋值于threshold,threshold为rehash的标志。在inflateTable中会算上loadFactor重新计算threshold的值。所以要避免HashMap使用过程出现rehash,initialCapacity不能直接传元素个数,initialCapacity必须大于等于(元素length/loadFactor),所以负载因子使用默认值0.75,初始容量设为1.333…*length,一般习惯1.4*length。

函数highestOneBit

在JDK1.6时,取最小的2的n次方使用while循环连续比较,在jdk1.7时如上做了改动。先取number值最高位,再向左移一位。highestOneBit实现:


public static int highestOneBit(int i) {
    // HD, Figure 3-1
    i |= (i >>  1);
    i |= (i >>  2);
    i |= (i >>  4);
    i |= (i >>  8);
    i |= (i >> 16);
    return i - (i >>> 1);
}

通过移位后i的值从最高位1开始都是1,在右移相减得到最高位的值。

本文来源:http://www.bbyears.com/wangyezhizuo/73829.html