HashMap源码解析(二)

成员变量

这里的table,就是所谓的 “桶” 的概念,存储 键/值 对。它的长度必须为2的次幂,原因不知。
具体一点,table的初始长度是 DEFAULT_INITIAL_CAPACITY(16),当存储数量的元素大于 16*0.75 时,会进行扩充容量,长度变为 1 << 4 (32)。

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;

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

// 桶,按需调整大小,长度为2的次幂(长度的限制为何如此?)
transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;

transient int size;

// 负载值,下次扩容的临界值,为:(capacity * load factor)
int threshold;

// 加载因子
final float loadFactor;

transient int modCount;

 /**
  * 哈希因子,避免哈希值重复;
  * 在initHashSeedAsNeeded  初始化,并用于 final int hash(Object k) 方法中,算key的哈希值
  */
transient int hashSeed = 0;

Holder

下面的代码还不太理解,据说是:
“ALTERNATIVE_HASHING_THRESHOLD_DEFAULT 针对与字符串的key,提供一个新的hash算法会提供更好的hashcode分布减少冲突,如果想启用尝鲜这个特性,你需要设置jdk.map.althashing.threshold这个系统属性的值为一个非负数(默认是-1)这个值代表了一个集合大小的threshold,超过这个值,就会使用新的hash算法。需要注意的一点,只有当re-hash的时候,新的hash算法才会起作用。

而Holder本身只是加载获取这个配置参数而已。”
而且在JDK8里面,这个方法已经被移除了,改用其他方式实现。
至于 java.security.AccessController.doPrivileged 可以参考 Spring源码–关于AccessController.doPrivileged

// 备选的负载值,以String作为Key时的备选负载值。
static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE;

private static class Holder {

    /**
     * Table capacity above which to switch to use alternative hashing.
     */
    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;

            // disable alternative hashing if -1
            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;
    }
}

构造方法

  1. 大多数情况下,我们都会使用不带参数的构造方法,这时的 负载值加载因子 都是默认的。如果要设置自己的 负载值加载因子,可以调用相应的构造方法。
  2. init()初始方法,是一个空的函数。
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);
}

// 构造一个映射关系与指定 Map 相同的新 HashMap。
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. 先看一下 Integer.highestOneBit 这个方法的作用:

    将一个整数(二进制)设置最高位为1,其它位为0,然后返回改变后的值;
    如果这个整数是0返回0。

  2. roundUpToPowerOf2 这个函数返回最接近 number 的2的次幂。如果 number 大于最大容量,则返回最大容量;如果 number 小于1,返回1;
    例如:3 -> 4, 4 -> 4, 5 -> 8。

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

inflateTable 这个方法,在调用put方法的时候也会用到,主要工作是创建了一个存放元素的Entry类。

private void inflateTable(int toSize) {
    // 获取容量为:最小的 >= toSize 的2次幂
    int capacity = roundUpToPowerOf2(toSize);

    // 计算出负载值
    threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
    // 创建大小为capacity的Entry
    table = new Entry[capacity];
    // 根据capacity计算hashSeed的值
    initHashSeedAsNeeded(capacity);
}

// 空的方法,子类可以重写该方法
void init() {}

// 这个方法不太理解。。。大意是依据capacity值计算出hashSeed,这个hashSeed用于以后计算hashCode
final boolean initHashSeedAsNeeded(int capacity) {
    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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值