本片文章不涉及到HashMap的TreeNode的相关代码。
先看到部分成员变量,常量
//存放数据的数组
transient Node<K,V>[] table;
//entry的set集合
transient Set<Map.Entry<K,V>> entrySet;
//HashMap中数据的个数
transient int size;
//HashMap中数据被修改次数,比如插入删除数据
transient int modCount;
//扩容阈值
int threshold;
//扩容因子
final float loadFactor;
//默认的初始容量
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
//table的最大长度
static final int MAXIMUM_CAPACITY = 1 << 30;
//默认的扩容因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//链表长度大于等于该值时,升级为红黑树
static final int TREEIFY_THRESHOLD = 8;
//红黑树的节点小于等于6时,退化为链表
static final int UNTREEIFY_THRESHOLD = 6;
//table长度小于该值时,即使某个table[i]的链表的长度大于8时,也不会升级为红黑树,而是去扩容
static final int MIN_TREEIFY_CAPACITY = 64;
看到构造方法
//可以指定容器HashMap的初始容量 和 扩容因子
//并不会初始化table
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;
//HashMap的容量必须为2的幂次方 , 使用initialCapacity计算出最接近的2的幂次方的值,作为扩容阈值
this.threshold = tableSizeFor(initialCapacity);
}
public HashMap(int initialCapacity) {
//使用DEFAULT_LOAD_FACTOR作为扩容因子
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
public HashMap() {
//使用DEFAULT_LOAD_FACTOR作为扩容因子 , threshold 并没有赋值
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
public HashMap(Map<? extends K, ? extends V> m) {
this.loadFactor = DEFAULT_LOAD_FACTOR;
//将m进行遍历,赋值到新的map
putMapEntries(m, false);
}
//有这种需求的可以直接调用HashMap提供的该方法
static final int tableSizeFor(int cap) {
//需要减一,如果该值本身为2的幂次方,则不需要扩大2倍
int n = cap - 1;
//连续做位移操作和或运算
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
//计算出的值即使超过了MAXIMUM_CAPACITY(最大长度),也只会返回MAXIMUM_CAPACITY
//计算出的值小于0,则返回1
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
看到put方法
public V put(K key, V value) {
//看到putValue方法
return putVal(hash(key), key, value, false, true);
}
//计算key的hash值
static final int hash(Object key) {
int h;
//key为null,则hash值为0
//调用key的hashCode方法,赋值给h
//返回 h ^ (h >>> 16)
//由于table的长度一般并不会很大, 当使用h &(table.length-1)计算该数据在table上的位置时,显然h高位的值 & 0全部为0,导致低位的值相同的而高位的值不同的key,落到了table的一个位置上。
//为了避免这种情况,需要让高位也参与到计算table位置,所以让高16位和低16位做了一次亦或运算来改变低位的值,高位的值则亦或上了16个0,等于没有发生变化。
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
//onlyIfAbsent如果为true,则key相同的情况下,不会替换老的value,如果为false,则用新的value替换老的value
//evict传入给afterNodeInsertion方法,HashMap的该方法为空实现。之后的文章讲到LinkedHashMap(重写了父类的afterNodeInsertion方法)的时候,再来看该方法。
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
//如果table为空,则初始化table,n为table的长度
if ((tab = table) == null || (n = tab.length) == 0)
//初始化table
n = (tab = resize()).length;
//使用 (table的长度减一) table的长度为2的幂次方 , 减去一 则变为 0...01...1 这种数据
//hash值 & (n-1) 则结果 必定在 0 到 长度减一 的范围 ,将该值赋值给i
//获取table的i位置赋值为p 如果p为空
if ((p = tab[i = (n - 1) & hash]) == null)
//将key,value包装为node,放入table[i]
tab[i] = newNode(hash, key, value, null);
//table的i位置已经有数据了
else {
Node<K,V> e; K k;
//如果p的hash和put进来的key的hash相同 且 key相同,则将p赋值给e
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
//如果该node为TreeNode , 已经从链表升级成为了红黑树 ,将该值添加到树中
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
//获取p的下一个节点,赋值给e
//如果e为空
if ((e = p.next) == null) {
//将key,value包装为node,将p的next设置为此node
p.next = newNode(hash, key, value, null);
//如果binCount 大于等于7 , 说明链表的长度大于等于8
// binCount从0开始 , 所以这里 8 -1 = 7
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
//将链表转化为红黑树,如果table的长度小于64,会调用resize扩容,不会转化为树
treeifyBin(tab, hash);
break;
}
//如果e的key和put进来的key相同 ,则跳出循环
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
//e赋值给p,接着获取后续节点
p = e;
}
}
//e不为空,说明存在相同的key
if (e != null) { // existing mapping for key
//获取e的值
V oldValue = e.value;
//onlyIfAbsent 为 false , 或者oldValue 为空 则将e的value改为put进来的value
if (!onlyIfAbsent || oldValue == null)
e.value = value;
//空方法交由子类实现
afterNodeAccess(e);
//返回老的value
return oldValue;
}
}
//modCount加1
++modCount;
//size加一
//如果size大于扩容阈值
if (++size > threshold)
//table扩容
resize();
//空方法交由子类实现
afterNodeInsertion(evict);
return null;
}
看到resize方法,初始化table或者扩容table
final Node<K,V>[] resize() {
//获取table
Node<K,V>[] oldTab = table;
//获取table的长度
int oldCap = (oldTab == null) ? 0 : oldTab.length;
//获取扩容阈值
int oldThr = threshold;
//新的容量,新的扩容阈值
int newCap, newThr = 0;
//如果oldCap大于0,说明table已经初始化过了,则进行扩容
if (oldCap > 0) {
//oldCap大于等于最大长度
if (oldCap >= MAXIMUM_CAPACITY) {
//扩容因子设置为Integer.MAX_VALUE
threshold = Integer.MAX_VALUE;
//已经最大了,没法扩容了,直接返回原本的table
return oldTab;
}
//newCap 设置为 oldCap的两倍
//如果newCap 小于最大长度 且 oldCap 大于等于 DEFAULT_INITIAL_CAPACITY(16)
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
//newThr 设置为oldThr的两倍
newThr = oldThr << 1; // double threshold
}
//如果oldThr 大于 0 ,指定了容量的HashMap的构造方法oldThr 大于 0
else if (oldThr > 0)
//newCap赋值为扩容阈值
newCap = oldThr;
//HashMap无参的构造方法 oldThr会为0
else {
//newCap 为 16
newCap = DEFAULT_INITIAL_CAPACITY;
//newThr 为 16 * 0.75 = 12
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
//如果newThr为0,
if (newThr == 0) {
//使用newCap * 扩容因子 计算 扩容阈值
float ft = (float)newCap * loadFactor;
//newCap 小于最大长度 且 扩容阈值 小于 最大长度 ,则扩容阈值为 (int)ft , 否则为Integer.MAX_VALUE
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
//赋值计算后扩容阈值给threshold 变量
threshold = newThr;
//实例化新的table
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
//oldTab 不为空 ,则进行扩容操作, 否则当前只是初始化table, 直接返回
if (oldTab != null) {
//遍历老的table
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
//如果oldTab[j]不为空,则要将数据迁移到newTab中
//oldTab[j]赋值给e
if ((e = oldTab[j]) != null) {
//置空原本的oldTab[j]
oldTab[j] = null;
//如果该位置只有一个数据,则将该数据的hash & newCap 计算出其在newTab上的位置,然后将newTable上对应位置的数据设置为e
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e;
//如果已经是一个红黑树,则迁移红黑树到newTable,如果迁移后的位置的数据量小于6,退化为链表
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
next = e.next;
//newCap为oldCap的2倍,则oldCap 0...10000 ->>> 0...01111 newCap 0...100000 ->>> 0...011111
//使用e的hash值& oldCap(此时并没有减一,该值为2的幂次方)
//则此时只有一个1(0...10000),则结果值只会有两种情况,key的hash值对应的位置上的值是否为1(由于落在了一个table的位置上,说明之前的低位 & (oldCap-1) 的值都是一样的,现在用前面高的一位 与运算 这个1,来区分原来的数据),其他位置的值 & 0 全部为0了)
//loHead直接可以放入newTab[j]
//hiHead直接可以放入newTab[j + oldCap]
//hash对应的位置上的值为0
if ((e.hash & oldCap) == 0) {
//如果还没有值,e赋值给loHead
if (loTail == null)
loHead = e;
//已经有值了,loTail下一个元素指向e
else
loTail.next = e;
//改变loTail为下一个元素
loTail = e;
}
//hash对应的位置上的值为1
else {
//如果还没有值,e赋值给hiHead
if (hiTail == null)
hiHead = e;
//已经有值了,hiTail下一个元素指向e
else
hiTail.next = e;
//改变hiTail为下一个元素
hiTail = e;
}
} while ((e = next) != null); //循环e的链表直至为空
//loTail 不为空
if (loTail != null) {
//loTail的next 置空 ,(这里的next 可能是 原本在oldTab的值高位为1的)
loTail.next = null;
//loHead赋值给newTab[j] ,
newTab[j] = loHead;
}
//hiTail 不为空, hiHead赋值给newTab[j+oldCap]
if (hiTail != null) {
//hiTail的next 置空 ,(这里的next 可能是 原本在oldTab的值高位为0的)
hiTail.next = null;
//hiHead赋值给newTab[j + oldCap]
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}
再来看putMapEntries方法
//putAll也调用了putMapEntries方法
public void putAll(Map<? extends K, ? extends V> m) {
putMapEntries(m, true);
}
final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
//获取m中数据个数
int s = m.size();
if (s > 0) {
//如果table 为空 , 需要初始化table
if (table == null) { // pre-size
//使用s 粗略计算 m的下一次的扩容阈值 +1为了下面的向上取整
float ft = ((float)s / loadFactor) + 1.0F;
//判断是否大于最大值,正常来说取值为(int)ft,舍弃了小数位
int t = ((ft < (float)MAXIMUM_CAPACITY) ?
(int)ft : MAXIMUM_CAPACITY);
//如果m的下一次的扩容阈值大于 原本map的扩容阈值
if (t > threshold)
//使用t计算出新的扩容阈值赋值给threshold 一个最靠近t的2的幂次方数
threshold = tableSizeFor(t);
}
//table不为空,则已经初始化过了, 对table进行扩容
else if (s > threshold)
resize();
//遍历m
for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
//获取m的 key value 放入map中
K key = e.getKey();
V value = e.getValue();
putVal(hash(key), key, value, false, evict);
}
}
}
接着看到remove方法
public V remove(Object key) {
Node<K,V> e;
//如果key存在map中,返回对应的value,否则返回null
return (e = removeNode(hash(key), key, null, false, true)) == null ?
null : e.value;
}
public boolean remove(Object key, Object value) {
return removeNode(hash(key), key, value, true, true) != null;
}
//matchValue为true,则需要value也相同,才会移除该元素
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;
//table不为空 ,table长度大于0 , 使用hash计算出的table的位置上不为空
//p为该hash值对应的table的位置上的节点
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;
//判断p的hash值和p的key 和 被移除的节点的hash和key 是否相同
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
//将p赋值给node
node = p;
//接着如果p的下一个节点e不为空,则接着寻找链表或者红黑树
else if ((e = p.next) != null) {
//如果是红黑树,从树上找到该节点返回
if (p instanceof TreeNode)
node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
//遍历链表寻找该节点
else {
do {
//判断e的hash值和p的key 和 被移除的节点的hash和key 是否相同
if (e.hash == hash &&
((k = e.key) == key ||
(key != null && key.equals(k)))) {
//将e赋值给node
node = e;
//找到了就跳出
break;
}
//p记录着被移除的节点的上一个节点
p = e;
} while ((e = e.next) != null);
}
}
//如果node 不为空, 则找到了被移除的节点
//如果matchValue 为true, 则还要判断节点的value 和 入参的 value 是否一样 才会移除该节点
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);
//如果node 和 p 相等 ,则说明被移除的节点是table位置上的头节点, 直接将table位置上的节点赋值为下一个节点即可
else if (node == p)
tab[index] = node.next;
//被移除节点是table位置上的非头的节点
else
//p此时是被移除的节点的上一个节点
//直接将p的下一个节点指向被移除节点的下一个节点
p.next = node.next;
//modCount加一 size减一
++modCount;
--size;
//空方法,交由子类实现
afterNodeRemoval(node);
//返回被移除的节点
return node;
}
}
return null;
}
看到get方法
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
//table不为空,table长度大于0, 使用hash计算出table位置上节点(first)不为空
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
//如果first的 hash 和 key 和 入参的 hash key相同 直接返回first
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
//如果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;
}
最后来看下EntrySet的iterator方法。
public final Iterator<Map.Entry<K,V>> iterator() {
//实例化一个新的EntryIterator对象
return new EntryIterator();
}
//继承了HashIterator
final class EntryIterator extends HashIterator
implements Iterator<Map.Entry<K,V>> {
//调用父类的nextNode方法
public final Map.Entry<K,V> next() { return nextNode(); }
}
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
expectedModCount = modCount;
//t被赋值为table
Node<K,V>[] t = table;
current = next = null;
index = 0;
//如果table不为空且HashMap中含有元素
if (t != null && size > 0) { // advance to first entry
//遍历t , 从t中找到一个不为空的节点赋值给next,然后跳出循环
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;
//e被赋值为next
Node<K,V> e = next;
//如果modCount 和 expectedModCount不等 ,抛异常
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (e == null)
throw new NoSuchElementException();
//将current赋值为e, 将next赋值为下一个节点, 如果next不为空 ,if不成立,直接返回e
if ((next = (current = e).next) == null && (t = table) != null) {
//如果next为空,寻找t的后面的位置上一个不为空的节点赋值给next
do {} while (index < t.length && (next = t[index++]) == null);
}
return e;
}
public final void remove() {
//获取current
Node<K,V> p = current;
if (p == null)
throw new IllegalStateException();
//如果modCount 和 expectedModCount不等 ,抛异常
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
//current 置为空
current = null;
//获取当前节点的key
K key = p.key;
//从HashMap中移除该节点 modCount加一了
removeNode(hash(key), key, null, false, false);
//修改expectedModCount为modCount (当使用Iterator遍历过程中,使用该方法移除元素,会修改expectedModCount 和modCount保持一致)
expectedModCount = modCount;
}
}
到此,HashMap的主要源码就解析完毕了。