数据结构之HashMap源码详解(五)get、remove、replace方法源码分析

第一篇文章传送门
第二篇文章传送门
第三篇文章传送门
第四篇文章传送门
get方法
get方法:

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

如果e为空,那么就返回null,不然就返回e.可以看到,主要方法是getNode方法。
getNode:

final HashMap.Node<K, V> getNode(int hash, Object key) {
       
        HashMap.Node[] tab;
        
        HashMap.Node first;
        
        int n;
        
        if ((tab = this.table) != null && (n = tab.length) > 0 && (first = tab[n - 1 & hash]) != null) {
            Object k;
           
            if (first.hash == hash && ((k = first.key) == key || key != null && key.equals(k))) {
                return first;
            }

            HashMap.Node e;
            
            if ((e = first.next) != null) {
                if (first instanceof HashMap.TreeNode) {
                    return ((HashMap.TreeNode)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;
    }

table:当前hashMap的散列表
first:桶位中的头元素
n:table数组长度
e:临时node元素。

if ((tab = this.table) != null && (n = tab.length) > 0 && (first = tab[n - 1 & hash]) != null)

第一个判断条件就是判断hashMap是否为空,第二个判断条件就是表示当前位置是否为空。

if (first.hash == hash && ((k = first.key) == key || key != null && key.equals(k))) {
                return first;
            }

如果桶位的第一个元素的hash与key都和要查询的一致,那么就表示找到了,直接返回。

if ((e = first.next) != null) 

如果有下一个,表示当前位置不止一个元素,可能为链表,也可能是红黑树。

if (first instanceof HashMap.TreeNode) {
                    return ((HashMap.TreeNode)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);

桶位形成了链表,如果链表上的第一个元素就是要查找的元素,那么就返回,不然就继续向下寻找。
remove方法

public V remove(Object key) {
        HashMap.Node e;
        return (e = this.removeNode(hash(key), key, (Object)null, false, true)) == null ? null : e.value;
    }

其实可以看到,是和get方法有异曲同工之妙的。它调用了removeNode方法来实现删除。
removeNode方法;

final HashMap.Node<K, V> removeNode(int hash, Object key, Object value, boolean matchValue, boolean movable) {
       
        HashMap.Node[] tab;
    
        HashMap.Node p;
      
        int n;
        
        int index;
      
        if ((tab = this.table) != null && (n = tab.length) > 0 && (p = tab[index = n - 1 & hash]) != null) {
           
            HashMap.Node<K, V> node = null;
            Object k;
           
            if (p.hash == hash && ((k = p.key) == key || key != null && key.equals(k))) {
                node = p;
            } else {
               
                HashMap.Node e;
                if ((e = p.next) != null) {
                 
                    if (p instanceof HashMap.TreeNode) {
                       
                        node = ((HashMap.TreeNode)p).getTreeNode(hash, key);
                    } else {
                       
                        label88: {
                            while(e.hash != hash || (k = e.key) != key && (key == null || !key.equals(k))) {
                                p = e;
                                if ((e = e.next) == null) {
                                    break label88;
                                }
                            }

                            node = e;
                        }
                    }
                }
            }

            Object v;
           
            if (node != null && (!matchValue || (v = ((HashMap.Node)node).value) == value || value != null && value.equals(v))) {
                
                if (node instanceof HashMap.TreeNode) {
                    ((HashMap.TreeNode)node).removeTreeNode(this, tab, movable);
                }
               
                else if (node == p) {
                    tab[index] = ((HashMap.Node)node).next;
                }
                
                else {
                    p.next = ((HashMap.Node)node).next;
                }

                ++this.modCount;
                --this.size;
                this.afterNodeRemoval((HashMap.Node)node);
                return (HashMap.Node)node;
            }
        }

        return null;
    }

tab:引用当前hashMap中的散列表
p:当前node元素
n:表示散列表长度
index:表示寻址结果
node:查找的结果
e:当前node的下一个元素

 if ((tab = this.table) != null && (n = tab.length) > 0 && (p = tab[index = n - 1 & hash]) != null)

判断hashMap不为空,并且路由的桶位是有数据的,需要进行查找操作。

if (p.hash == hash && ((k = p.key) == key || key != null && key.equals(k))) {
                node = p;
            }

这种是最理想的情况,当前桶位中的元素就是要删除的元素,不用向下进行寻找。

else {
                
                HashMap.Node e;
                if ((e = p.next) != null) {
                    /
                    if (p instanceof HashMap.TreeNode) {
                       
                        node = ((HashMap.TreeNode)p).getTreeNode(hash, key);
                    }

如果当前桶位没有要删除的元素,并且后面还有元素,那么就有两种情况,链表或者红黑树。如果是红黑树,那么就执行红黑树的查找操作。

else {
                        
                        label88: {
                            while(e.hash != hash || (k = e.key) != key && (key == null || !key.equals(k))) {
                                p = e;
                                if ((e = e.next) == null) {
                                    break label88;
                                }
                            }

                            node = e;
                        }
                    }

不为红黑树的话,那么就是链表结构,按照链表的顺序一个一个向下进行寻找,知道找到hash和key都相同的节点。
删除操作:

  if (node != null && (!matchValue || (v = ((HashMap.Node)node).value) == value || value != null && value.equals(v)))

如果node不为空,那么就说明按照key查找到需要删除的数据了。

 if (node instanceof HashMap.TreeNode) {
                    ((HashMap.TreeNode)node).removeTreeNode(this, tab, movable);
                }

如果是红黑树,那么就走树的删除逻辑。

else if (node == p) {
                    tab[index] = ((HashMap.Node)node).next;
                }

p==node表示第一个桶位就找到了,那么就删除。
删除方法:将下一个节点的值赋值给现在位置就可以。

else {
                    p.next = ((HashMap.Node)node).next;
                }

这个则是剩下的最后一种情况:链表。将当前元素p的下一个元素设置成 要删除的元素node 的下一个元素。
replace方法

public V replace(K key, V value) {
        HashMap.Node e;
        if ((e = this.getNode(hash(key), key)) != null) {
            V oldValue = e.value;
            e.value = value;
            this.afterNodeAccess(e);
            return oldValue;
        } else {
            return null;
        }
    }

replace方法就相对简单多了,就是根据传入的key,找到对应的node节点,进行替换即可。

public boolean replace(K key, V oldValue, V newValue) {
        HashMap.Node e;
        Object v;
        if ((e = this.getNode(hash(key), key)) == null || (v = e.value) != oldValue && (v == null || !v.equals(oldValue))) {
            return false;
        } else {
            e.value = newValue;
            this.afterNodeAccess(e);
            return true;
        }
    }

根据key找到对应的node节点,将新的value替换老的value。getNode方法在前面已经介绍过了,这里就不再介绍了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值