HashMap源码探究

HashMap的创建
public HashMap() {
        table = (HashMapEntry<K, V>[]) EMPTY_TABLE;
        threshold = -1; // Forces first put invocation to replace EMPTY_TABLE
    }
    public HashMap(int capacity) {
        if (capacity < 0) {
            throw new IllegalArgumentException("Capacity: " + capacity);
        }

        if (capacity == 0) {
            @SuppressWarnings("unchecked")
            HashMapEntry<K, V>[] tab = (HashMapEntry<K, V>[]) EMPTY_TABLE;
            table = tab;
            threshold = -1; // Forces first put() to replace EMPTY_TABLE
            return;
        }

        if (capacity < MINIMUM_CAPACITY) {
            capacity = MINIMUM_CAPACITY;
        } else if (capacity > MAXIMUM_CAPACITY) {
            capacity = MAXIMUM_CAPACITY;
        } else {
            capacity = Collections.roundUpToPowerOfTwo(capacity);
        }
        makeTable(capacity);
    }
    public HashMap(int capacity, float loadFactor) {
        this(capacity);

        if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
            throw new IllegalArgumentException("Load factor: " + loadFactor);
        }

        /*
         * Note that this implementation ignores loadFactor; it always uses
         * a load factor of 3/4. This simplifies the code and generally
         * improves performance.
         */
    }

构造函数中涉及到了capacity、loadFactor两个参数,这两个参数是影响HashMap性能的重要参数。

capacity代表了创建哈希表的初始容量,loadFactor代表哈希表容量自动增加之前可以达到多满,它衡量的是一个散列表的空间的使用程度,负载因子越大表示散列表的装填程度越高,反之愈小。对于使用链表法的散列表来说,查找一个元素的平均时间是O(1+a),因此如果负载因子越大,对空间的利用更充分,然而后果是查找效率的降低;如果负载因子太小,那么散列表的数据将过于稀疏,对空间造成严重浪费。系统默认负载因子为0.75

==事实上虽然第三个构造函数中可以指定loadFactor但是实际HashMap实现中并没有使用参数中的值而只是使用了默认的0.75==

image

如图:HashMap底层实现为数组table,数组的每一项又是一个链表HashMapEntry,而构造函数中capacity指的就是数组的长度

    /**
     * Allocate a table of the given capacity and set the threshold accordingly.
     * @param newCapacity must be a power of two
     */
    private HashMapEntry<K, V>[] makeTable(int newCapacity) {
        @SuppressWarnings("unchecked") HashMapEntry<K, V>[] newTable
                = (HashMapEntry<K, V>[]) new HashMapEntry[newCapacity];
        table = newTable;
        threshold = (newCapacity >> 1) + (newCapacity >> 2); // 3/4 capacity
        return newTable;
    }
    static class HashMapEntry<K, V> implements Entry<K, V> {
        final K key;
        V value;
        final int hash;
        HashMapEntry<K, V> next;

        HashMapEntry(K key, V value, int hash, HashMapEntry<K, V> next) {
            this.key = key;
            this.value = value;
            this.hash = hash;
            this.next = next;
        }
        ...
    }
HashMap的数据存入
    @Override public V put(K key, V value) {
        //key为空时 调用putValueForNullKey方法单独处理,这也是为什么HashMap支持null数据存取的原因
        if (key == null) {
            return putValueForNullKey(value);
        }
        //计算key的hash值
        int hash = Collections.secondaryHash(key);
        HashMapEntry<K, V>[] tab = table;
        //!!!计算hash对应的数组位置(h&(length - 1)就相当于对length取模,而且速度比直接取模快得多,这是HashMap在速度上的一个优化。)
        int index = hash & (tab.length - 1);
        //tab是否存在该hash值,如果存在则在数组对应的链表中查找是否有对应的key,如果有则新值覆盖旧值
        for (HashMapEntry<K, V> e = tab[index]; e != null; e = e.next) {
            //存在hash值和key值均相同的数据,执行新值覆盖旧值的动作
            if (e.hash == hash && key.equals(e.key)) {
                preModify(e);
                V oldValue = e.value;
                e.value = value;//新值覆盖旧值
                return oldValue;//返回旧值
            }
        }
        //上面没有找到对应的key值 创建一个
        // No entry for (non-null) key is present; create one
        modCount++;
        //判断是否需要扩容
        if (size++ > threshold) {
            tab = doubleCapacity();
            index = hash & (tab.length - 1);
        }
        //数组添加新的节点
        addNewEntry(key, value, hash, index);
        return null;
    }
HashMap的读取
    public V get(Object key) {
        //key为null时,调用entryForNullKey得到对应的value
        if (key == null) {
            HashMapEntry<K, V> e = entryForNullKey;
            return e == null ? null : e.value;
        }
        //计算key对应的hash值
        int hash = Collections.secondaryHash(key);
        HashMapEntry<K, V>[] tab = table;
        //查找tab是否存在对应的key,如果存在返回对应的value
        for (HashMapEntry<K, V> e = tab[hash & (tab.length - 1)];
                e != null; e = e.next) {
            K eKey = e.key;
            if (eKey == key || (e.hash == hash && key.equals(eKey))) {
                return e.value;//返回查找的value
            }
        }
        return null;
    }

HashMap的底层数组长度总是2的n次方,在构造函数中存在:capacity <<= 1;这样做总是能够保证HashMap的底层数组长度为2的n次方。当length为2的n次方时,h&(length - 1)就相当于对length取模,而且速度比直接取模快得多,这是HashMap在速度上的一个优化

HashMap中需要做到尽量的分布均匀且充分利用空间,这块的算法后面单说。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值