前面增加和查询都解析完了,这里我们看一下跟删除相关的方法。
Remove key
1 2 3 4
| public V remove(Object key) { Node<K,V> e; return (e = removeNode(hash(key), key, null, false, true)) == null ? null : e.value; }
|
通过源码可以看到,主要的功能方法还是里面的removeNode方法,我们看一下removeNode方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| 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; }
|
这里是remove方法的源码,虽然注释也写了很多,但是我们还是一步一步来分析一遍。
1
| if ((tab = table) != null && (n = tab.length) > 0 && (p = tab[index = (n - 1) & hash]) != null) {}
|
首先还是前提条件要保证:① 数组不为null ② 数组的长度大于0 ③ 计算到的hash值对应的数组位置节点不为null。【index计算请看put方法相应文章】
继续:
其实我们会发现,查询和删除都有相同的一步,就是需要先把这个key指定的节点找出来。再去执行后面的操作。所以我们可以看到,removeNode前面一部分代码和查询的代码是相似的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 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); } }
|
同样情景也是分为三种:
- 数组上的节点就是目标节点
- 此时链表已经是树结构,需要通过搜索红黑树来查找目标节点
- 数组上的桶对应是链表结构,则需要通过遍历链表来查找目标节点
具体细节就不再赘述,跟前面get方法一直。
继续:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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; }
|
执行完查询之后就是需要执行具体的删除操作了。
首先需要判断指定的节点是否存在
不存在直接返回null
存在;则同样需要分为三种情况处理
- 红黑树结构,则执行removeTreeNode方法进行删除【不深究】
- 数组上的位置就是目标节点。即node与p相等,则直接将node的next放置在数组第i个位置上。
- 如果不是在数组的第i位置,则需要通过段链的方式来删除节点,即此时节点p是node的上一个节点,只需要把p的next执行node的next即可。
我们再回过头来看这个remove方法的声明:
1
| public V remove(Object key)
|
只传入一个key,来删除,即不用通过比较value的值,遇到相同的key就删除,所以在调用removeNode的时候,matchValue参数传入的是false,即这个值为false,!matchValue就为true,则后面的值value就不用比较了。
1
| if (node != null && (!matchValue || (v = node.value) == value || (value != null && value.equals(v))))
|
Remove key value
我们再看一下传入两个值的方法
1 2 3
| public boolean remove(Object key, Object value) { return removeNode(hash(key), key, value, true, true) != null; }
|
通过前面的分析,我们大概可以猜到,这个方法移除条件是建立在key和value都相同的时候,才移除节点。所以matchValue传入的是true。此时的!matchValue就是false,所以需要看后面value判断是否为true。
1
| if (node != null && (!matchValue || (v = node.value) == value || (value != null && value.equals(v))))
|
至此,HashMap的remove方法就解析这么多了