一、结构
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {
继承AbstractMap抽象类。HashMap是一种Map其用了3种结构(jdk8)来存储对应的key-value元素,即数组、节点Node,红黑树,同时key-value元素会构建一个Node类。数组用来确定[ 不同元素的hash值与数组长度的且运算 ] (要明白这里,只是计算所得的index相同,而不是这两个元素本身的hash值相同,hash值还是唯一的)来确定对应的位置。
因此这样的话,就会有两个问题:当存储多个元素的话就可能在hash值与数字长度且元素所得到的数组index的位置是相同的,这个时候就需要用到Node类的next属性来讲其放到原来的index位置元素的下面,以此就是一个链了,其实可见将整个链表看成数组中的一个元素、第二个问题就是,我们一直往hashMap中放元素,如果计算的index一直相同,就会放在一个index位置,同时一直往这个index位置的链表一直往后面添加,我们再查询对应元素的时候其实查询的复杂的就可能为n了,所以JDK优化了,当链表的长度超过8(默认值)就将链表结构转换为树(红黑树)结构来存储,这样就能加加快查询速度(我们本次会先跳过红黑树的细节分析,这个到整理算法的时候再解析)。
二、变量
1、DEFAULT_INITIAL_CAPACITY
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
HashMap的初始化默认容量大小 16。
2、MAXIMUM_CAPACITY
static final int MAXIMUM_CAPACITY = 1 << 30;
最大的容量大小。
3、DEFAULT_LOAD_FACTOR
static final float DEFAULT_LOAD_FACTOR = 0.75f;
默认的加载因子 (用来计算设置扩容的阈值,例如初始化容量是16 ,然后16*0.75 = 12 ,也就是说当设置)。
4、TREEIFY_THRESHOLD
booleanstatic final int TREEIFY_THRESHOLD = 8;
当链的长度达到此值就将其转换为红黑树。
5、table;
transient Node<K,V>[] table;
存放 [元素hash值与table长度且运算所得的index]第一个计算到该index的元素。
6、entrySet;
transient Set<Map.Entry<K,V>> entrySet;
存放的是HashMap中所有的key-value元素。
7、size;
transient int size;
这个表示当前Map有多少个key-value键值对。
8、modCount
transient int modCount
对Map中元素修改的次数。
9、threshold
int threshold;
扩容的阈值,例如我们前面举例的16*0.72=12,次数这个threshold就是threshold,当前面的size达到12的时候就会触发对table数组的扩容。
10)、loadFactor
final float loadFactor;
加载因子,同前面DEFAULT_LOAD_FACTOR的意义,只是DEFAULT_LOAD_FACTOR表示的是一个具体的值,不过在初始化的时候会将DEFAULT_LOAD_FACTOR赋值给loadFactor。
三、构造方法
1、HashMap(int initialCapacity, float loadFactor)
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
}
/**
* Returns a power of two size for the given target capacity.
*/
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
这个就是传入初始化容量&扩容加载因子,再通过initialCapacity计算threshold,这里的tableSizeFor元素其是就是获取进为(n + 1),例如你传入7或6,由于右移的对应操作,就会将右边的都变为1,然后再+1就达到了进位,所以就会返回8。
2、HashMap(int initialCapacity)
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
调用的第一个构造方法,如果加载因子会传入默认的DEFAULT_LOAD_FACTOR。
3、HashMap()
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
只初始化了loadFactor。
4、HashMap(Map<? extends K, ? extends V> m)
public HashMap(Map<? extends K, ? extends V> m) {
this.loadFactor = DEFAULT_LOAD_FACTOR;
putMapEntries(m, false);
}
也可以直接传入一个Map来进行初始化。
我们可以看到在1/2/3中与前面的那些集合类有些不同,其并没有对table数组进行对应的初始化。
四、内部类
1、Node
1)、结构&变量&构造方法
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
可以看到其实现Map接口所对应的Map.Entry。这个Node就用来存储key-value元素的,可以看到其有四个成员变量:hash-对应key的hash值、key-对应传入的key、value-对应传入的value值、next-此节点的下一个节点(用来处理当多个元素hashCode与数组长度且运算所得的index相同是,这时候就用链表来建立其的关系)。
2)、方法
public final K getKey() { return key; }
public final V getValue() { return value; }
public final String toString() { return key + "=" + value; }
public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
public final boolean equals(Object o) {
if (o == this)
return true;
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
if (Objects.equals(key, e.getKey()) &&
Objects.equals(value, e.getValue()))
return true;
}
return false;
}
可以看到其重写了toString&equals&hashCode方法。在equals方法中其是同时调用key&value两者的equals方法。
2、KeySet
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) {
......
}
}
这个是用来放key集合的,其继承AbstractSet,但里面的方法是直接调用的Hashmap的方法。
3、Values
final class Values extends AbstractCollection<V> {
public final int size() { return size; }
public final void clear() { HashMap.this.clear(); }
public final Iterator<V> iterator() { return new ValueIterator(); }
public final boolean contains(Object o) { return containsValue(o); }
public final Spliterator<V> spliterator() {
return new ValueSpliterator<>(HashMap.this, 0, -1, 0, 0);
}
public final void forEach(Consumer<? super V> action) {
......
}
}
这里与上面类似其是继承的AbstractCollection,其是这个与上一篇的AbstractMap类似。
4、EntrySet
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;
}
......
}
这个也与前面两个类似,只是EntrySet是用来遍历获取Entry的。这里需要提到的,我们再前面文章是有梳理一些集合的抽象实现的,例如AbstractCollection,其的add方法并没有具体显示,而是需要子类实现,其本身是抛出UnsupportedOperationException异常。
public boolean add(E e) {
throw new UnsupportedOperationException();
}
所以其实我们主要是用这些EntrySet、Values的迭代器去遍历其的值,而并不能进行添加操作(这样操作也不符号节点的添加逻辑)。
5、HashIterator
1)、结构&成员变量&构造方法
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);
}
}
对Node节点遍历的的迭代器实现。可以看到在其的初始化方法中,通过do-while遍历按顺序找到table数组的最前面的一个node节点赋值给next,因为计算hashCode 对应的index是散列的。要注意这里的index变量,其是表示的当前遍历到了table数组的那个槽位。
2)、方法
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;
removeNode(p.hash, p.key, null, false, false);
expectedModCount = modCount;
}
我们看下nextNode方法,其是通过next这个节点遍历,如果其后面next还有其它的节点则往下,如果此index 对应的链表遍历完了,则再进行index++到下一个槽位,以此来完成对所有节点的遍历。而remove方法则是直接调用的HashMap的方法。
6、KeyIterator
final class KeyIterator extends HashIterator
implements Iterator<K> {
public final K next() { return nextNode().key; }
}
迭代器对节点的key遍历获取。
7、ValueIterator
final class ValueIterator extends HashIterator
implements Iterator<V> {
public final V next() { return nextNode().value; }
}
迭代器对节点的value遍历获取。
8、EntryIterator
final class EntryIterator extends HashIterator
implements Iterator<Map.Entry<K,V>> {
public final Map.Entry<K,V> next() { return nextNode(); }
}
对节点(Entry | Node)进行遍历,可以看到其只是将nextNode包装为next方法。
9、TreeNode
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
TreeNode<K,V> parent; // red-black tree links
TreeNode<K,V> left;
TreeNode<K,V> right;
TreeNode<K,V> prev; // needed to unlink next upon deletion
boolean red;
TreeNode(int hash, K key, V val, Node<K,V> next) {
super(hash, key, val, next);
}
这里还有红黑树的实现,不过这个我们到以后整理算法相关的内容在来分析。同时这里这些集合类都是由流操作的,不过这个我们下一个系列补上。两个大坑
五、方法
1、hash(Object key)
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
这里就是计算key对应的hashCode的值。可以看到其是对原来key的hashCode再进行了一次异或操作。
2、put(K key, V value)
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
将key-value对应的元素添加到map中。
3、newNode
Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) {
return new Node<>(hash, key, value, next);
}
根据hash值&key&value以及其的下一个节点创建一个对应的Node节点
4、afterNodeAccess&afterNodeInsertion&afterNodeRemoval
// Callbacks to allow LinkedHashMap post-actions
void afterNodeAccess(Node<K,V> p) { }
void afterNodeInsertion(boolean evict) { }
void afterNodeRemoval(Node<K,V> p) { }
这3个方法是空的其是用来给HashMap的这里linkedHashMap实现操作的。
5、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) {
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
}
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
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;
if (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;
}
这是对当前数据进行扩容,是HashMap中一个重要方法,我们来具体分析下。
1)、首先是将当前数组table的长度赋值给oldCap
表示以前数组的容量
int oldCap = (oldTab == null) ? 0 : oldTab.length;
2)、再将扩容的阈值threshold
赋值给oldThr
表示原来的扩容阈值
int oldThr = threshold;
3)、再定义新的容量&阈值变量newCap
、newThr
int newCap, newThr = 0;
4)、然后再判断原来的容量oldCap
进行对应赋值
if (oldCap > 0) {
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
}
这里的oldCap
如果>0表示已经对table数组进行初始化了,这里首先是判断原来容量oldCap
是否已经达到最大值了,如果>=MAXIMUM_CAPACITY MAXIMUM_CAPACITY = 1 << 30
了,则本次将扩容阈值threshold
变为Integer的最大值,然后返回原数组。不然将原来的容量左移扩大为原来的两倍赋值给newCap
新容量,同时newThr
也在原来的基础上将其左移位。
5)、如果原来的oldThr
扩容阈值大于0&oldCap
不大于0(需要不满足前面的if条件),则将原来的扩容阈值作为本次的容量。(构造方法的选择会出现这种情况)
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
6)、上面两种都不满足就是下面这种逻辑,容量设置的默认容量,扩容阈值也是用的默认的加载因子DEFAULT_LOAD_FACTOR
0.75
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
7)、如果newThr
经过上面逻辑为0(例如走的上面的else if逻辑),则再计算其值计算float ft = (float)newCap * loadFactor;
,不过这里是不是可以直接提到else if中?看上面的赋值其他的一般不会造成newThr
为0?
8)、将计算所得的newThr
赋值给threshold
,再根据新的newCap创建对应数组,并赋值给成员变量table
。
threshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
扩容后需要进行对应订单节点转移查询分配对应的位置。不过这里的前提是if (oldTab != null)
以前的table
数组是初始化了的,然后遍历以前的容量长度oldCap
获取数据重新分配
9)、遍历看该index位置是否有值e = oldTab[j]) != null
并赋值给e
,有值则进行处理
if (oldTab != null) {
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
if ((e = oldTab[j]) != null) {
10)、将原来该位置置为null(该位置节点的内容已经赋值给e
了),如果e
元素其没有next下一个元素了表明该位置只有一个Node,则通过e.hash & (newCap - 1)
计算新的槽位设置到newTable
中。
oldTab[j] = null;
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e;
11)、如果该e
的next有另外的节点,则先判断该节点是否已经由简单点的链表转换为了TreeNode红黑树了,是的话就去使用TreeNode方法的去处理(这里我们跳过)。
else if (e instanceof TreeNode)
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
12)、如果是普通的链表就走该逻辑
else { // preserve order
13)、这一块的逻辑主要是需要明白其的四个局部变量:loHead
-低位的头节点、loTail
低位的尾节点、hiHead
-高位的头节点、hiTail
-高位的尾结点。
现在我们来说明下这个高位、低位:首先我们假设原来table数组长度是4二进制是100
(index槽位为 0-3,所以我们可以看到前面在进行key.hashCode与数组长度的且运算是需要对数组长度进行减1操作的,即对应的二进制是11
),现在对table扩容由4变为81000
,此时的index就是0-7 111
。我们假设两个key的hash值是0011
、0111
,这两个key在原来容量4对应的11
与其进行且运算都是11
,index为3。现在扩容了,容量变为8 对应的最大index是111
,这个时候0011
的且运算就是311
,而0111
的且运算就变为了7111
,同时7=原来index3+原来的容量4,重新计算的3就是本次的低位、7就是本次计算的高位。所以如果原来容量4形成的链表其的Node在第3位有些为1或0,在新的容量的index 3与index 7会形成两个新的链表,这两个链表会有自己的head节点、tail节点。
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;
}
上面代码loHead
&loTail
,就是描叙形成的低位链表,可以看到在后面的赋值是if (loTail != null) {
、newTab[j] = loHead;
而hiHead
&hiTail
是描叙的高位,其的赋值是if (hiTail != null) {
、newTab[j + oldCap] = hiHead;
。同时这里判断进位的高低位是通过if ((e.hash & oldCap) == 0)
,判断,例如我们有4扩容到8,这里的oldCap就是4100
,将其与hash且运算就能判断第3位是否为1了,以此就能区分高低位了。
到这里扩容后的元素重新分配就完成了。
6、putVal(...)
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
这个方法就是将key-value元素放到map中。
1)、实现判断table数组是否完成初始化化创建,如果没有创建则通过resize方法去创建,n
表示数组长度,i
表示table的index。
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
2)、table完成初始化创建后(如果需要),通过i = (n - 1) & hash]
计算本次添加的index位置(并将其赋值给p
),如果该位置还没有值,则通过newNode方法创建一个Node节点添加在该位置。如果该位置有值了,则是另外的处理了。
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
3)、判断前面赋值的index位置节点p的hash值是否等于本次添加的元素的hash值&key相等,如果相等,将其赋值给e
(表示已经存在该节点了),这个e
表示的是根据key-value创建的本次将要添加的Node节点。
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
4)、如果当前节点已经变为了TreeNode,则是用TreeNode去处理
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
5)、如果是普通链表,则是循环链表找到对应位置进行添加
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
遍历链表,看其有没有下一个,同时还通过hash&key判断再这个链表中有没有对应的节点,像前面的判断一样,如果有则不需要创建新的节点,直接break。如果next没有了,通过newNode创建对应的节点赋值添加给p,添加到此index位置链表的最后面,同时如果已经超过TREEIFY_THRESHOLD - 1
了,则将该链表转换为TreeNode。
6)、如果e!= null
,表示已经存在该节点了,这里先判断如果是只有存在才添加,就不用新的value来替代原来的value,如果不是就替代(这里如果原来是null即使是存在才添加也会添加),然后调用afterNodeAccess方法,最后将原来的值返回。
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
7)、修改+1、size加1,如果size已经达到了扩阈值threshold
,则扩容,然后调用afterNodeInsertion方法。
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
7、getNode(int hash, Object key)
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;
}
根据hash&key获取对应Node的value。这里先是通过(n - 1) & hash
找到对应的index的第一个节点,在遍历判断hash&key是否相等。
8、get(Object key)
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
根据key获取对应节点的值,可以看到这里是调用的getNode方法,同时hash传入的是通过hash方法再进行key的hash值。
9、containsKey(Object key)
public boolean containsKey(Object key) {
return getNode(hash(key), key) != null;
}
通过getNode方法看获取的value是不是null。
10、size()
public int size() {
return size;
}
已经存放了多少key-value键值对了。
11、removeNode(...)
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;
}
这一块就是删除对应节点,matchValue
入参是表示进行节点删除的时候,节点相等判断不单是要判断hash&key,还要判断value,另一个movable
是用于TreeNode的。这里先根据index = (n - 1) & hash
获取index位置的第一个Node。
1)、先判断第一个node是否已经相等了,如果是则将其赋值给node(node = p
)
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
node = p;
2)、(省略了TreeNode部分),遍历判断,找到对应的Node,这里要注意如果找到了是直接break
,只有node = e;
,没有p = e;
,表示其不是第一个元素。
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);
}
3)、上面提到的没有p = e
也就是node != p
,表明其不是第一个元素,就是直接将前一个节点p
的next去连接找到的对应node
的next,这样就直接删除了node。如果node == p
,表示是第一个,则直接是node.next
(其为null)赋值给tab[index]
,来达到删除node。
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;
}
12、remove(Object key)
public V remove(Object key) {
Node<K,V> e;
return (e = removeNode(hash(key), key, null, false, true)) == null ?
null : e.value;
}
删除key对应的node,其会返回node对应的value,调用的removeNode方法。
13、clear()
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;
}
}
清空,可以看到直接简单粗暴的进行tab[i] = null;
。
14、containsValue(Object value)
public boolean containsValue(Object value) {
Node<K,V>[] tab; V v;
if ((tab = table) != null && size > 0) {
for (Node<K, V> e : tab) {
for (; e != null; e = e.next) {
if ((v = e.value) == value ||
(value != null && value.equals(v)))
return true;
}
}
}
return false;
}
是不是含有这个value。直接遍历的table数组。
15、keySet()
public Set<K> keySet() {
Set<K> ks = keySet;
if (ks == null) {
ks = new KeySet();
keySet = ks;
}
return ks;
}
key对应集合
16、values()
public Collection<V> values() {
Collection<V> vs = values;
if (vs == null) {
vs = new Values();
values = vs;
}
return vs;
}
value值集合
17、entrySet()
public Set<Map.Entry<K,V>> entrySet() {
Set<Map.Entry<K,V>> es;
return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}
所有Node节点集合。
18、capacity()
final int capacity() {
return (table != null) ? table.length :
(threshold > 0) ? threshold :
DEFAULT_INITIAL_CAPACITY;
}
容量获取。