HashMap源码分析(二)
resize重置大小
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;//旧容量
int oldThr = threshold;//旧阈值
int newCap, newThr = 0;
if (oldCap > 0) {//如果旧容量大于0
if (oldCap >= MAXIMUM_CAPACITY) {//是否大于最大容量
threshold = Integer.MAX_VALUE;//阈值设置为最大整数
return oldTab;//因为已经达到最大容量,所以直接返回旧tab
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&//如果旧容量大于0且小于最大容量,新容量=旧容量*2
oldCap >= DEFAULT_INITIAL_CAPACITY)//如果旧容量大于等于默认的容量
newThr = oldThr << 1; // double threshold //新阈值扩大一倍
}
else if (oldThr > 0) // initial capacity was placed in threshold //如果oldCap<=0即tab为空,则初始化容量为阈值
newCap = oldThr;
else { // zero initial threshold signifies using defaults //如果oldCap和oldThr都为0
newCap = DEFAULT_INITIAL_CAPACITY;//新容量=默认初始化容量
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);//新阈值=默认装载因子*默认初始化容量
}
if (newThr == 0) {//如果新阈值=0
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);//新阈值=ft或者最大值
}
threshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
if (oldTab != null) {
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
if ((e = oldTab[j]) != null) {
oldTab[j] = null;//置为null便于GC回收
if (e.next == null)//如果e.next=null即到了尾节点
newTab[e.hash & (newCap - 1)] = e;
else if (e instanceof TreeNode)//不是尾节点且是按照树存储的
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // preserve order //
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
next = e.next;
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}
重置容量的操作还是比较复杂的。并未完全理解。
删除操作
public V remove(Object key) {
Node<K,V> e;
return (e = removeNode(hash(key), key, null, false, true)) == null ?
null : e.value;
}
/**
* Implements Map.remove and related methods
*
* @param hash hash for key
* @param key the key
* @param value the value to match if matchValue, else ignored
* @param matchValue if true only remove if value is equal
* @param movable if false do not move other nodes while removing
* @return the node, or null if none
*/
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;
}
分析:
final Node < K , V > removeNode(int hash, Object key, Object value,boolean matchValue, boolean movable) |
参数:
hash : key的hash值
key : 键值
value : 值
matchValue : 是否匹配特定值(remove(key,value)中为true)
movable : 按树的方式移除时的参数,作用暂时不知
删除思路:
对于hashmap的节点删除是这样的:首先进行hashmap的非空判断,不为空则根据hash值判断是否要删除的是头节点(头节点存储在tab[]数组中),如果是则用node存储待删除的节点。如果要删除的不是头节点,则判断存储方式(是链表还是树?),如果是树则按照树的方式删除,如果是链表则按链表的方式删除。在按照链表的方式删除时,遍历链表找到要删除的节点,然后用node存储待删除的节点。这要找到要删除的节点node时就执行删除操作:首先判断node是否为空,并且判断是否需要匹配value,然后判断node是不是TreedNode的实例,如果是则按照树删除,否则按照链表删除,(注意:如果删除的链表的头节点则需要更改tab[]数组里的头节点值)链表的删除操作比较简单:断开带删除节点bnode的两侧链,然后node的前驱的next指向node的后继节点即可。
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;//移动指针(p是node的前驱结点)
} while ((e = e.next) != null);
}
}
if (node != null && (!matchValue || (v = node.value) == value ||
(value != null && value.equals(v)))) {//如果node不为null,并且不用匹配值(或者匹配值,如果匹配值,则在使用的时候就是remve(key,value))
if (node instanceof TreeNode)//如果node是TreedNode的实例
((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);//按照树的方式移除
else if (node == p)//如果移除的是头节点
tab[index] = node.next;//则tab内的头节点改为被移除的node节点的下一个节点
else
p.next = node.next;//p的前驱指向node的后继,重新将链表连起来
++modCount;//操作次数加1
--size;//map大小减1
afterNodeRemoval(node);//移除后的操作
return node;//返回移除的节点
}
}
return null;
}
清空操作
情况操作比较简单,就是对table[]数组赋值为null即可
public void clear() {
Node<K,V>[] tab;
modCount++;
if ((tab = table) != null && size > 0) {
size = 0;
for (int i = 0; i < tab.length; ++i)
tab[i] = null;
}
}
判断key是否存在
因为调用的getNode()方法,直接计算hash值,时间复杂度为O(1)。
public boolean containsKey(Object key) {
return getNode(hash(key), key) != null;
}
判断value是否存在
先进行非空判断,然后遍历table[]数组,获取到头节点,然后再根据头节点遍历链表即可。时间复杂度为O(n)
public boolean containsValue(Object value) {
Node<K,V>[] tab; V v;
if ((tab = table) != null && size > 0) {
for (int i = 0; i < tab.length; ++i) {
for (Node<K,V> e = tab[i]; e != null; e = e.next) {
if ((v = e.value) == value ||
(value != null && value.equals(v)))
return true;
}
}
}
return false;
}
KeySet
获取key的set集合。这里有一个KeySet类,里边有常用的iterator()方法获取迭代器。
public Set<K> keySet() {
Set<K> ks = keySet;
if (ks == null) {
ks = new KeySet();
keySet = ks;
}
return ks;
}
final class KeySet extends AbstractSet<K> {
public final int size() { return size; }
public final void clear() { HashMap.this.clear(); }
public final Iterator<K> iterator() { return new KeyIterator(); }
public final boolean contains(Object o) { return containsKey(o); }
public final boolean remove(Object key) {
return removeNode(hash(key), key, null, false, true) != null;
}
public final Spliterator<K> spliterator() {
return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
}
public final void forEach(Consumer<? super K> action) {
Node<K,V>[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (int i = 0; i < tab.length; ++i) {
for (Node<K,V> e = tab[i]; e != null; e = e.next)
action.accept(e.key);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
}
entrySet
和keySet类似。
public Set<Map.Entry<K,V>> entrySet() {
Set<Map.Entry<K,V>> es;
return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}
final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
public final int size() { return size; }
public final void clear() { HashMap.this.clear(); }
public final Iterator<Map.Entry<K,V>> iterator() {
return new EntryIterator();
}
public final boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>) o;
Object key = e.getKey();
Node<K,V> candidate = getNode(hash(key), key);
return candidate != null && candidate.equals(e);
}
public final boolean remove(Object o) {
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>) o;
Object key = e.getKey();
Object value = e.getValue();
return removeNode(hash(key), key, value, true, true) != null;
}
return false;
}
public final Spliterator<Map.Entry<K,V>> spliterator() {
return new EntrySpliterator<>(HashMap.this, 0, -1, 0, 0);
}
public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
Node<K,V>[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (int i = 0; i < tab.length; ++i) {
for (Node<K,V> e = tab[i]; e != null; e = e.next)
action.accept(e);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
}
可以看到这两个类里的forEach()方法里都判断了mc和modCount是否相等,如果不想等则说明在遍历的时候map的数据被修改了,会抛出ConcurrentModificationException异常
1.8之后新加的
//获取值,如果key不存在则返回dafaultValue
@Override
public V getOrDefault(Object key, V defaultValue) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? defaultValue : e.value;
}
//仅当key不存在或者value=null时插入,如果key存在且value不为null则什么都不干(不会覆盖)
@Override
public V putIfAbsent(K key, V value) {
return putVal(hash(key), key, value, true, true);
}
//移除键值对
@Override
public boolean remove(Object key, Object value) {
return removeNode(hash(key), key, value, true, true) != null;
}
//替换
@Override
public boolean replace(K key, V oldValue, V newValue) {
Node<K,V> e; V v;
if ((e = getNode(hash(key), key)) != null &&
((v = e.value) == oldValue || (v != null && v.equals(oldValue)))) {
e.value = newValue;
afterNodeAccess(e);
return true;
}
return false;
}
//替换
@Override
public V replace(K key, V value) {
Node<K,V> e;
if ((e = getNode(hash(key), key)) != null) {
V oldValue = e.value;
e.value = value;
afterNodeAccess(e);
return oldValue;
}
return null;
}
迭代器
HashIterator是HashMap的迭代器类。里边有常用的hasNext()、remove(),获取下一个节点的nextNode()方法。同样通过if (modCount != expectedModCount)来抛出异常。
abstract class HashIterator {
Node<K,V> next; // next entry to return //下一个节点
Node<K,V> current; // current entry //当前节点
int expectedModCount; // for fast-fail //
int index; // current slot
HashIterator() {
expectedModCount = modCount;
Node<K,V>[] t = table;
current = next = null;
index = 0;
if (t != null && size > 0) { // advance to first entry
do {} while (index < t.length && (next = t[index++]) == null);
}
}
public final boolean hasNext() {
return next != null;
}
final Node<K,V> nextNode() {
Node<K,V>[] t;
Node<K,V> e = next;
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (e == null)
throw new NoSuchElementException();
if ((next = (current = e).next) == null && (t = table) != null) {
do {} while (index < t.length && (next = t[index++]) == null);
}
return e;
}
public final void remove() {
Node<K,V> p = current;
if (p == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
current = null;
K key = p.key;
removeNode(hash(key), key, null, false, false);
expectedModCount = modCount;
}
}
到这里HashMap的主要源码(常用的)就结束了,至于红黑苏结构的存储方式日后再论。