JDK 1.8 HashMap 源码阅读二

本文深入探讨HashMap的工作原理,包括如何通过处理key的hashcode减少碰撞、get方法的具体实现及删除键值对的不同方式。

HashMap中对key的hashcode再处理

由上篇文章我们已知HashMap的寻址是让key的hashcode与bucket长度取模。如果bucket长度为16,即只有hashcode的低四位参与了寻址。这大大加大的碰撞的发生。
HashMap中为了让碰撞几率减小,让hashcode更多位参与寻址。他做了如下处理:
    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
即key的hashcode保留高16位,低16位的值为高16位与低16位的异或值。 这样处理,让hashcode的高16在bucket长度小的情况下也能参与寻址以减小碰撞的可能。

HashMap中Get方法的实现

其Get方法是通过Key值来获得对应的value值,如下代码:
    final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
            if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
            if ((e = first.next) != null) {
                if (first instanceof TreeNode)
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        return null;
    }
由上诉代码我们可知:
-Get方法应用到了key的hashcode方法和equals方法。自定义类作为key时,需要实现这两方法。
-Get方法在取value后,并没有删除该键值对。即插入键值对,若无手动remove,可重复取用。
-Get方法并未过滤key为null的情况,同时也未应用到value的值。即支持key/value为null的情形。

HashMap中删除键值对

JDK中提供了2中删除方式:
    public V remove(Object key) {
        Node<K,V> e;
        return (e = removeNode(hash(key), key, null, false, true)) == null ?
            null : e.value;
    }
以及
    @Override
    public boolean remove(Object key, Object value) {
        return removeNode(hash(key), key, value, true, true) != null;
    }
从中可以看出,不同处是方法一只对key做关键字删除,而方法二是同时推键值对做关键字删除。
但是其底层实现是同一函数的,只是参数不同而已。

    final Node<K,V> removeNode(int hash, Object key, Object value,
                               boolean matchValue, boolean movable) {
        Node<K,V>[] tab; Node<K,V> p; int n, index;
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (p = tab[index = (n - 1) & hash]) != null) {
            Node<K,V> node = null, e; K k; V v;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                node = p;
            else if ((e = p.next) != null) {
                if (p instanceof TreeNode)
                    node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
                else {
                    do {
                        if (e.hash == hash &&
                            ((k = e.key) == key ||
                             (key != null && key.equals(k)))) {
                            node = e;
                            break;
                        }
                        p = e;
                    } while ((e = e.next) != null);
                }
            }
            if (node != null && (!matchValue || (v = node.value) == value ||
                                 (value != null && value.equals(v)))) {
                if (node instanceof TreeNode)
                    ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
                else if (node == p)
                    tab[index] = node.next;
                else
                    p.next = node.next;
                ++modCount;
                --size;
                afterNodeRemoval(node);
                return node;
            }
        }
        return null;
    }
由上代码可知,其主要分为2个步骤,查找和删除。
其查找的实现就是Get方法的实现,而删除的操作就是从链表中摘除掉一个节点,或从红黑树中摘除一节点。


最后的说明

本人对HashMap的阅读并未深入到树形存储部分,因为红黑树的算法实现和结构是另一回事了,如要说明又是一篇很长的篇幅。
同时在阅读HashMap的源码时,我并未看到其有多线程的同步操作处理。即HashMap是非线程安全的。
需要线程安全,可以考虑HashTable和ConCurrentHashMap。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值