默认初始是16
负载因子
收缩大小
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
volatile V val; // 这个用volatile修饰 保证了并发时候的可见性
volatile Node<K,V> next;
Node(int hash, K key, V val, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.val = val;
this.next = next;
}
public final K getKey() { return key; }
public final V getValue() { return val; }
public final int hashCode() { return key.hashCode() ^ val.hashCode(); }
public final String toString(){ return key + "=" + val; }
public final V setValue(V value) {
throw new UnsupportedOperationException();
}
public final boolean equals(Object o) {
Object k, v, u; Map.Entry<?,?> e;
return ((o instanceof Map.Entry) &&
(k = (e = (Map.Entry<?,?>)o).getKey()) != null &&
(v = e.getValue()) != null &&
(k == key || k.equals(key)) &&
(v == (u = val) || v.equals(u)));
}
/**
* Virtualized support for map.get(); overridden in subclasses.
*/
Node<K,V> find(int h, Object k) {
Node<K,V> e = this;
if (k != null) {
do {
K ek;
if (e.hash == h &&
((ek = e.key) == k || (ek != null && k.equals(ek))))
return e;
} while ((e = e.next) != null);
}
return null;
}
}
一个内部类 Node<K,V>具体作用就是存放 Node节点的数据
第一个table 存放node节点的数据采用懒加载 总是2的幂次方 第二个是存放扩容时新生成的数据
第四个是 一直都是2的幂次方 这个的意思是 每次都向上取整 取的是2的幂次方
就是 1.5 * initialCapacity + initialCapacity + 1 然后是向上取整 取 2的幂次方
/** Implementation for put and putIfAbsent */
final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException();
int hash = spread(key.hashCode());// 计算hash值
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
tab = initTable();// 如果table为空的话就先初始化 一般是初始化一个
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) { // f拿到的是头结点
if (casTabAt(tab, i, null, // 如果头结点没有元素的话就直接用cas进行插入就好了
new Node<K,V>(hash, key, value, null)))
break; // no lock when adding to empty bin
}
else if ((fh = f.hash) == MOVED) // 如果有别的正在进行扩容的操作 那么就先扩容
tab = helpTransfer(tab, f);
else {
V oldVal = null;
synchronized (f) { // 锁住第一个节点(链表头)
if (tabAt(tab, i) == f) { // 如果是链表
if (fh >= 0) { // 哪一个节点 Node 头节点的hash值肯定大于0
binCount = 1;// 最少从第一个节点开始 所以底下是++
for (Node<K,V> e = f;; ++binCount) {
K ek;
if (e.hash == hash &&
((ek = e.key) == key || 判断hash的最基本的条件 就是hash值是否相同 然后判断这个键 或者 要考虑键为空的情况 然后再进行添加
(ek != null && key.equals(ek)))) {
oldVal = e.val;
if (!onlyIfAbsent) // 是否只在节点不存在的时候才加
e.val = value;
break;
}
Node<K,V> pred = e;
if ((e = e.next) == null) { 如果说是在链表末尾就直接添加
pred.next = new Node<K,V>(hash, key,
value, null);
break;
}
}
}
else if (f instanceof TreeBin) { 不是链表就是树
Node<K,V> p;
binCount = 2;
if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
value)) != null) {
oldVal = p.val;
if (!onlyIfAbsent)
p.val = value;
}
}
}
}
if (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD) 判断是不是要转为红黑树
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
addCount(1L, binCount);
return null;
}
变成红黑树的时候 判断一下是否大于 64 如果小于64 就不扩容 否则则 扩容 扩容操作的时候还需要对头节点进行加锁 这个就是扩容方法
数据迁移方法
transfer 极其复杂 而且也只是迁移了一部分