HashMap:由浅入深学习HashMap源码(1)

JDK 17的HashMap 提供了30个 public 方法。

Hash table 实现了 map接口。HashMap 和 Hash table大致相同,但是他是线程不安全的并且允许 null 值以及 null key。

Hash Map能够在常量的时间内实现get 和 put操作。

HashMap(int initialCapacity, float loadFactor)

输入初始容量,以及装载因子。其中初始容量最大为 2的30次方。值得注意的是,HashMap并不是按照指定的容量进行初始化,而是经过 tableSizeFor(initialCapacity) 操作后计算得出,大于 指定值且最接近指定值的 2的幂。也就意味着,HashMap的容量总是2的幂。
值得注意的是,HashMap的内存初始化容量确定是在第一次put操作,这点后面会进行讲解。

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;
    this.threshold = tableSizeFor(initialCapacity);
}

HashMap(int initialCapacity)

实际上调用的是HashMap(int initialCapacity, float loadFactor)。这里的装载因子默认为0.75f

HashMap()

只会设置默认的装载因子。

上面两个构造函引出了问题。什么时候确定容量呢?

HashMap(Map<? extends k>, ? extends V> m)

可以使用另一个Map来构造一个HashMap。默认装载因子为0.75.

V Put(K key, V value)

这里最常用的put方法其实是有返回值的,根据输入的key,如果key之前关联有value则返回value,如果没有则返回null。

实际上我们可以继续分析Put的具体实现,观察HashMap是如何来实现一个放入的具体函数实现。

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

让我们继续分析putval函数

hash(key)

我们观察到在放入的时候需要先对key进行hash。如果传入的key为null的话,那么返回0.如果传入的key不是null,那么返回一个根据key的hashcode处理后的hash。这样做的目的是为了让产生的hash更为分散。

 static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

putVal(int hash, K key, V value, boolean onlyIfAbset, boolean evict)

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict)

这里传入的参数为 key处理后的hash以及 key value。 两个判断状态。onlyIfAbsent为true的时候表示不要改变现在存入的value,evict如果为false表示这个表在creation mode(没有明白)

我们从put方法中可以看到,调用putVal时将OnlyIfAbsent传入false 表示可以更改其中存储的value,并且不在creation mode。

继续观察putVal的源码 (代续)。

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        // 这里发现了新类 Node。
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值