这篇文章有点长,建议看下去
public 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);
//负载因子必须是一个大于0的数字
this.loadFactor = loadFactor;//首先负载因子进行赋值
this.threshold = tableSizeFor(initialCapacity);
}
函数isNaN是Float类实现方法,用于判断一个值是否Not-a-Number。
如果以上都成立,调用tableSizeFor函数,产生一个大于给定容量的最小的2的某次方。
static final int tableSizeFor(int cap) {
int n = -1 >>> Integer.numberOfLeadingZeros(cap - 1);
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
//>>>带符号右移操作,用0补位
//>>不带符号右移,符号位为1则用1补位。
numberOfLeadingZeros函数是Integer类实现的,用于数一个数前面有多少位是0
public static int numberOfLeadingZeros(int i) {
// HD, Count leading 0's
if (i <= 0)
return i == 0 ? 32 : 0;
int n = 31;
if (i >= 1 << 16) { n -= 16; i >>>= 16; }
if (i >= 1 << 8) { n -= 8; i >>>= 8; }
if (i >= 1 << 4) { n -= 4; i >>>= 4; }
if (i >= 1 << 2) { n -= 2; i >>>= 2; }
return n - (i >>> 1);
}
可以看出,以上函数在求i前面有多少位0(可以带一个数试一下)
cap从0开始:
cap=0时,i=-1,最高位是1,前面没有0,n=-1,threshold=1
cap=1时,i=0,有32位0,-1带符号右移32位等同于右移0位,n=-1,threshold=1
cap=2时,i=1,有31位0,-1带符号右移31位,变为0**01,n=1,threshold=2
cap=3时,i=2,有30位0,-1右移30位,变为0 ** 11,n=2,threshold=4
cap=5时,i=4,有29位0,-1右移29位,变为0 **111,n=7,threshold=8
(注意-1在32位二进制中表示为:11111111 11111111 11111111 11111111)
这里要注意的是HashMap并不是在产生新对象的时候就确定真实容量和阈值,而是在写入第一组键值对(Node)的时候才确定这个map的容量和阈值。上面求出来的threshold仅仅是一次赋值。
在写入map的时候都会判断容量并调用resize()函数。
resize()函数用于对哈希table进行初始化和扩容,在第一次写入Node时进行初始化,就是利用之前求出来的loadFactor和threshold。
resize函数的解析见:https://blog.csdn.net/weixin_44893585/article/details/103638977
利用this关键字调用第一种构造方式
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
无参构造方式会在table初始化的时候调用resize函数,并利用默认容量和负载因子进行初始化。
public HashMap(Map extends K, ? extends V> m) {
this.loadFactor = DEFAULT_LOAD_FACTOR;
putMapEntries(m, false);//把字典加到HashMap里的方法
}
putMapEntries是把字典加到HashMap里的方法
final void putMapEntries(Map extends K, ? extends V> m, boolean evict) {
int s = m.size();
if (s > 0) {
if (table == null) { //说明table还没初始化
float ft = ((float)s / loadFactor) + 1.0F;
int t = ((ft < (float)MAXIMUM_CAPACITY) ?(int)ft : MAXIMUM_CAPACITY);
if (t > threshold)
threshold = tableSizeFor(t);
}
else {//当s > threshold,需要重新散列resize
while (s > threshold && table.length < MAXIMUM_CAPACITY)
resize();
}
//table已经初始化
//或者根据输入字典的长度重新散列之后
//或者输入字典的s e : m.entrySet()) {
K key = e.getKey();
V value = e.getValue();
putVal(hash(key), key, value, false, evict);//调用最初的putVal函数进行put见下文
}
}
}
这里的hash(key)函数的内容是
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}//hashcode 和Hashcode带符号右移16位相异或,为的是减少冲突。
Hashcode的源码解析参考链接:
https://blog.csdn.net/weixin_44893585/article/details/103638755
putVal就是把Node放入哪个桶,更具体的,放到链表的哪个位置上的函数
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
HashMap.Node[] tab; HashMap.Node p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)//如果table为空
n = (tab = resize()).length;//利用resize进行初始化,并将初始化后的Node数组的长度赋值给n
if ((p = tab[i = (n - 1) & hash]) == null)//(n - 1) & hash = hash%n,是一种高效的取余运算方法,方便求索引(放入哪个桶)
tab[i] = newNode(hash, key, value, null);//如果是空桶,就放一个节点,且next=null
else {//说明不是空桶
HashMap.Node e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))//要put的Node和原有的Node是相同的
e = p;//e就指向这个桶
else if (p instanceof HashMap.TreeNode)
e = ((HashMap.TreeNode)p).putTreeVal(this, tab, hash, key, value);
else {//与原有的Node不相同
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);//p.next为空,直接放进去,e指向此桶
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.next值相同,说明不用再放一次了
p = e;
}
}//上面还没有放入新的Node,只是确定了存放位置
if (e != null) { //e引用刚才确定好的Node节点
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)//onlyIfAbsent if true, don't change existing value只有值是false,才会对改动原来的Node的值
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;//The number of times this HashMap has been structurally modified
if (++size > threshold)
resize();
afterNodeInsertion(evict);//void afterNodeInsertion(boolean evict) { }没有执行任何操作
return null;
}
afterNodeAccess(e);这一句没有执行操作
afterNodeInsertion(evict);这一句也没有执行任何操作
void afterNodeAccess(Node p) { }
void afterNodeInsertion(boolean evict) { }
其实这两个函数是为了HashMap的子类LinkedHashMap准备的,此处无具体执行内容
final HashMap.Node getNode(int hash, Object key) {//返回一个Node
HashMap.Node[] tab; HashMap.Node 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 HashMap.TreeNode)
return ((HashMap.TreeNode)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;
}
getNode函数如果找到了就返回找到的那个节点,没找到则返回null
final HashMap.Node removeNode(int hash, Object key, Object value,
boolean matchValue, boolean movable) {//返回一个Node节点
HashMap.Node[] tab; HashMap.Node p; int n, index;
if ((tab = table) != null && (n = tab.length) > 0 &&
(p = tab[index = (n - 1) & hash]) != null)
{ HashMap.Node 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 HashMap.TreeNode)
node = ((HashMap.TreeNode)p).getTreeNode(hash, key);
else{
do {
if (e.hash == hash &&
((k = e.key) == key ||
(key != null && key.equals(k)))) {
node = e;//当找到的时候会把e.next赋给node
break;//并结束循环,不给p赋值,所以p指向待删节点的前节点
}
p = e;//不管有没有执行if,都会把e.next赋给p
} while ((e = e.next) != null);
}
}//将匹配到要删除的节点赋值给p,node
if (node != null && (!matchValue || (v = node.value) == value ||
(value != null && value.equals(v)))) {
if (node instanceof HashMap.TreeNode)
((HashMap.TreeNode)node).removeTreeNode(this, tab, movable);
else if (node == p)//说明要删除的Node是头节点
tab[index] = node.next;
else //要删除的Node不是头节点
p.next = node.next;//将目标节点的前驱元的后继元指向目标节点的后继元,则node节点被略过
++modCount;
--size;
afterNodeRemoval(node);
return node;
}
}
return null;
}