java之HashMap源码(二)

64 阅读2分钟

上一篇我们讲了HashMap的put方法,这篇讲解get方法。

public V get(Object key) {
    Node<K,V> e;
    return (e = getNode(hash(key), key)) == null ? null : e.value;
}

可以看到get方法里面调用了getNode()方法

final Node<K,V> getNode(int hash, Object key) {
    //tab数组, first数组的元素或者链表的第一个节点,e当前节点, n数组大小, k节点的键
    Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
    //判断数组不为空并且key所在的位置不为空
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (first = tab[(n - 1) & hash]) != null) {
        //如果数组的元素或者链表的第一个节点的key与传进来的key相匹配,返回first
        if (first.hash == hash && // always check first node
            ((k = first.key) == key || (key != null && key.equals(k))))
            return first;
         //如果下一个节点不为null
        if ((e = first.next) != null) {
            if (first instanceof TreeNode) //如果时红黑树,调用getTreeNode(hash, key)
                return ((TreeNode<K,V>)first).getTreeNode(hash, key);
             //不是红黑树,遍历链表
            do {
                //比较key
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    return e;
            } while ((e = e.next) != null);
        }
    }
    return null;
}

getNode(int hash, Object key)有两个参数,第一个时key的hash值,第二个时key。 首选判断数组是否为空,如果为空,则放回null, 如果数组不为空,根据key的hash值计算出key在数组中的位置,当前位置为null,则返回null,否则判断两者的key是否相等,相等返回当前节点。不相等,如果是链表进行do-while循环,比较每一个节点的key,相等则返回,如果是红黑树,调用getTreeNode(hash, key)。

final TreeNode<K,V> getTreeNode(int h, Object k) {
    return ((parent != null) ? root() : this).find(h, k, null);
}

如果parent != null返回包含这个节点的树的根否则返回this,其实就是找到树的根节点,然后调用find方法遍历树,根据key找到对应的树节点

final TreeNode<K,V> find(int h, Object k, Class<?> kc) {
    TreeNode<K,V> p = this;
    do {
        //局部变量
        int ph, dir; K pk;
        TreeNode<K,V> pl = p.left, pr = p.right, q;

        if ((ph = p.hash) > h)  //key的hash值小于当前树节点的hash值
            p = pl; //将左孩子赋值给p,遍历左树
        else if (ph < h) //key的hash值大于当前树节点的hash值
            p = pr; //将右孩子赋值给p,遍历右树
        //key匹配则返回当前树节点
        else if ((pk = p.key) == k || (k != null && k.equals(pk)))
            return p;
        else if (pl == null) //如果当前节点的左孩子为空,则将当前节点的右孩子赋值给p,遍历右树
            p = pr;
        else if (pr == null)
            p = pl;
        //如果kc不等于null或者(key实现了Comparable接口并且key通过compare比较之后的值不等于0)
        else if ((kc != null ||
                  (kc = comparableClassFor(k)) != null) &&
                 (dir = compareComparables(kc, k, pk)) != 0)
            //dir < 0,key不匹配
            p = (dir < 0) ? pl : pr; 
        else if ((q = pr.find(h, k, kc)) != null)
            return q;
        else
            p = pl;
    } while (p != null);
    return null;
}

红黑树获取对应key的值,采用了二分查找,key的hash值大于根节点遍历右子树,小于则遍历左子树。使用了两种比较方法,一种是==和equals匹配,另一种是通过比较器Comparable进行匹配。