您的位置:首页 > 编程语言 > Java开发

JDK 1.8 HashMap 源码阅读二

2017-03-11 12:40 507 查看

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。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  hashmap java 源码