浅析HashMap源码系列----get过程(JDK1.8版)

本文深入解析HashMap中的get方法实现原理,包括如何通过key的Hash值定位到桶位、链表和红黑树的遍历方式等核心内容。
1 HashMap中get方法概述
  1. 通过key的Hash找到唯一的桶位。寻找方法和put过程中是相同的,(capacity-1)&hash
  2. 找到具体桶位,现在有两种情况
    1. 如果首元素的key和目标key相同,则返回首元素。
    2. 如果首元素的key不相同。判断有没有第二个元素
      1. 如果没有,在该桶位处就没必要再找,直接返回null。
      2. 如果有第二元素。判断首元素类型
        1. 如果为链表,采用do-while循环遍历桶中链表。
        2. 如果为红黑树,用红黑树的遍历方式getTreeNode();
          1. 首先找到根节点Root,从根节点向下找。
          2. 然后,根据查找key的hash值和当前node的hash值比较
            1. 如果大于,就向右边找。
            2. 如果小于,就向左找。
            3. 如果相同并比较equals相同,就返回当前节点
            4. 如果左孩子没有了,就向右孩子找。
            5. 如果右孩子没有了,就向左孩子找。
            6. 如果没有找到就进入下一轮递归寻找。
2 具体源码解析
2.1 get()
// // get过程是根据key的Hash找到具体的Node节点。直接返回node.value
 public V get(Object key) {
        Node<K,V> e;
        return (e = getNode(hash(key), key)) == null ? null : e.value;
    }
2.2 getNode() 获取目标key的Node
final Node<K,V> getNode(int hash, Object key) {
    Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
    // 判断node数组已经初始化,根据key的hash找到first node
    if ((tab = table) != null 
        && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) {
        // 判断桶上第一个node的hash和key的hash一致,并判断key的值和first node 的key是否相同,相同就返回第一个Node
        if (first.hash == hash && ((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);
            // 链表方式查找Node: 采用do while形式遍历,保证循环至少走一次。 
            do {
                // 依次遍历,和遍历桶顶元素
                if (e.hash == hash && 
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    return e;
            } while ((e = e.next) != null);
        }
    }
    return null;
}
2.3 红黑树查找Node的方式getTreeNode()
// root() 方法比较简单,循环判断TreeNode没有父节点,就将当前节点返回
final TreeNode<K,V> getTreeNode(int h, Object k) {
    return ((parent != null) ? root() : this).find(h, k, null);
}
// 从Root节点的查找方式
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;
        // 插入node的key.hash小的向左找
        if ((ph = p.hash) > h)
            p = pl;
        // 插入node的key.hash大的往右找
        else if (ph < h)
            p = pr;
        // 如果node的key完全一样,返回当前节点。
        else if ((pk = p.key) == k || (k != null && k.equals(pk)))
            return p;
        // 如果没有左孩子,就从右孩子找。
        else if (pl == null)
            p = pr;
        // 如果没有右孩子,就从左孩子找。
        else if (pr == null)
            p = pl;
        // key的Class在第一次使用会缓存下来,其中dir就是判断需要找的node在当前节点的左还是右边,
        // 插入也如此。
        else if ((kc != null || (kc = comparableClassFor(k)) != null) && (dir = compareComparables(kc, k, pk)) != 0)
            p = (dir < 0) ? pl : pr;
        // 递归调用find方法
        else if ((q = pr.find(h, k, kc)) != null)
            return q;
        else
            p = pl;
    } while (p != null);
    return null;
}	
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值