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的情形。
-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。