Map m = Collections.synchronizedMap(new HashMap(...));
大致相当于 hashtable, 只是hashmap不同步, 并允许空值;
这是hashmap的内部结构,用数组加链表的形式,先使用散列,把节点分布到数组的每个位置,发生冲突时,使用链表解决
这里散列的大小为2^n,事实上这并不是一个很好的选择,碰撞概率会增大。一般情况下,散列的大小最好取2的n次方-1(素数)。hashmap这样做是为了之后运算(位运算)方便,同时在hash时选择更好的hash函数,以抵消2的n次方带来的不便。
这是每一个node:
当链表长度大于8时,每一个node都会变成treeNode,形成红黑树。
hashmap中的字段如下,可以在初始化时进行设置,如果不设置,则按照默认的处理
//大小为2^n,首次使用时初始化,有时长度可以为0
transient Node<K,V>[] table;
//缓存节点,AbstractMap字段在keySet() and values()中使用
transient Set<Entry<K,V>> entrySet;
//map中存储节点数量
transient int size;
//版本号,结构修改时增加,fail-fast机制
transient int modCount;
//大于该值,rehash
int threshold;
//负载因子
final float loadFactor;
默认配置
//默认初始化容量
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
//最大容量
static final int MAXIMUM_CAPACITY = 1 << 30;
//负载因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//链表长度大于该值,转为红黑树
static final int TREEIFY_THRESHOLD = 8;
//最多可存储数量:CAPACITY * LOAD_FACTOR。大于该值,rehash。
static final int UNTREEIFY_THRESHOLD = 6;
//变成树的最小容量
static final int MIN_TREEIFY_CAPACITY = 64;
先得到key的hashcode,然后让高16位和低16位异或,结果就是hash,
index = (n - 1) & hash,也就是hash对表大小取余。
/*计算hash
由于map的大小为2^n,更容易出现碰撞,所以需要高位与低位异或,减少碰撞
*/
static final int hash(Object key) {
int h;
// >>>:无符号右移16位
//高位与低位异或
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
afterNodeInsertion,afterNodeAccess这些是linkhashmap会做的事情,此处不讨论
/** 存入值
* @param onlyIfAbsent if true, don't change existing value
* @param evict if false, the table is in creation mode.
* @return previous value, or null if none
*
*/
//todo
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为空,resize
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;//table的长度
//该节点应该存入的位置为空,新建节点,存入
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
//不为空,p指向链表或红黑树
else {
Node<K,V> e; K k;
//判断第一个节点,如果第一个节点就是要存储的节点,将p的值给e
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;
}
//如果e是要存储的节点,停止
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;
}
jdk1.7加入节点用的是头插法,所以tab[index] = 最新加入的节点,因为认为最新加入的节点用到的可能性会更大。
jdk1.8采用尾插
final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
//table为null,table长度为0,index = (n - 1) & hash,对应位置为null
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
//first = table[index],此位置存的是一串链表
//first存的是第一个节点
//先判断第一个节点(first)是不是
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;
}
经过这两个方法其他方法基本都大同小异,先得到index对应的链表/树,根据不同的情况进行处理。是树,交给树处理,是链表遍历,自行处理。
//扩容
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; // 两倍
}
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
else { //初始化
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;
}
jdk1.7:使用头插法,扩容时,链表会逆置
链表逆置可以避免尾部遍历,但存在一个很严重的问题:多线程的时候会导致死循环!!!
若线程1与线程2同时扩容,此时线程1与线程2都指向node1,且next节点均为node2。
线程1执行线程2休眠,线程1扩容完成后链表逆置原本的1->2->3变为3->2->1
线程2唤醒开始执行扩容,此时线程2指向node1,且next节点为node2,但node2的next节点又为node1,出现死循环
jdk1.8:采用尾插法,不会出现链表逆置
除此之外,jdk1.8的改进:在1.7扩容时,需要重新rehash,1.8不用。
if ((e.hash & oldCap) == 0){
loTail...
} else {
hiTail...
}
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
扩容,就是给原来的容量乘2,也就是把原来容量oldCap左移一位,这时2^n的好处就表现出来了。
index = (n - 1) & hash,如果这一位是0,则index = index,否则,index = index + oldCap。
这样很快就能得到新的index而且避免了rehash(rehash是一个消耗比较大的方法,避免它,可以提高性能)。
/**
* 添加一个节点,若该节点已存在,不改变原来的值
* */
@Override
public V putIfAbsent(K key, V value) {
return putVal(hash(key), key, value, true, true);
}
/**
* 有值,设置新值,返回新值
* 无值,头插
* */
//TODO afterNodeInsertion mappingFunction.apply treeifyBin afterNodeInsertion
@Override
public V computeIfAbsent(K key,
Function<? super K, ? extends V> mappingFunction) {
if (mappingFunction == null)
throw new NullPointerException();
int hash = hash(key);
Node<K,V>[] tab; Node<K,V> first; int n, i;
int binCount = 0;
TreeNode<K,V> t = null;
Node<K,V> old = null;
//设置大小resize
if (size > threshold || (tab = table) == null ||
(n = tab.length) == 0)
n = (tab = resize()).length;
//获取旧节点
if ((first = tab[i = (n - 1) & hash]) != null) {
if (first instanceof TreeNode)
old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
else {
Node<K,V> e = first; K k;
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
old = e;
break;
}
++binCount;
} while ((e = e.next) != null);
}
//旧的值和节点都存在,返回旧的值
V oldValue;
if (old != null && (oldValue = old.value) != null) {
afterNodeAccess(old);
return oldValue;
}
}
V v = mappingFunction.apply(key);
//新值不存在
if (v == null) {
return null;
} else if (old != null) {//新值存在且旧节点存在
old.value = v;//设置新值
afterNodeAccess(old);
return v;
}
//按照红黑树处理
else if (t != null)
t.putTreeVal(this, tab, hash, key, v);
else {//头插法给链表加入一个节点
tab[i] = newNode(hash, key, v, first);
if (binCount >= TREEIFY_THRESHOLD - 1)
treeifyBin(tab, hash);
}
++modCount;
++size;
afterNodeInsertion(true);
return v;
}
/**
* 新值存在则设置新值,不存在 则删除节点
* */
public V computeIfPresent(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
if (remappingFunction == null)
throw new NullPointerException();
Node<K,V> e; V oldValue;
int hash = hash(key);
if ((e = getNode(hash, key)) != null &&
(oldValue = e.value) != null) {
V v = remappingFunction.apply(key, oldValue);
if (v != null) {
e.value = v;
afterNodeAccess(e);
return v;
}
else
removeNode(hash, key, null, false, true);
}
return null;
}
@Override
public V compute(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
if (remappingFunction == null)
throw new NullPointerException();
int hash = hash(key);
Node<K,V>[] tab; Node<K,V> first; int n, i;
int binCount = 0;
TreeNode<K,V> t = null;
Node<K,V> old = null;
if (size > threshold || (tab = table) == null ||
(n = tab.length) == 0)
n = (tab = resize()).length;
//获取旧节点
if ((first = tab[i = (n - 1) & hash]) != null) {
if (first instanceof TreeNode)
old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
else {
Node<K,V> e = first; K k;
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
old = e;
break;
}
++binCount;
} while ((e = e.next) != null);
}
}
V oldValue = (old == null) ? null : old.value;
V v = remappingFunction.apply(key, oldValue);
if (old != null) {//有节点
if (v != null) {//新值存在
old.value = v;//设置新值
afterNodeAccess(old);
}
else//无值,删除节点
removeNode(hash, key, null, false, true);
}
//没有节点,但新值存在则添加节点
else if (v != null) {
//红黑树添加
if (t != null)
t.putTreeVal(this, tab, hash, key, v);
//链表添加
else {
tab[i] = newNode(hash, key, v, first);
if (binCount >= TREEIFY_THRESHOLD - 1)
treeifyBin(tab, hash);
}
++modCount;
++size;
afterNodeInsertion(true);
}
return v;
}
@Override
public V merge(K key, V value,
BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
if (value == null)
throw new NullPointerException();
if (remappingFunction == null)
throw new NullPointerException();
int hash = hash(key);
Node<K,V>[] tab; Node<K,V> first; int n, i;
int binCount = 0;
TreeNode<K,V> t = null;
Node<K,V> old = null;
//设置大小resize
if (size > threshold || (tab = table) == null ||
(n = tab.length) == 0)
n = (tab = resize()).length;
//first为对应的链表/树
//得到需要的节点old
if ((first = tab[i = (n - 1) & hash]) != null) {
//红黑树
if (first instanceof TreeNode)
old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
else {//链表
Node<K,V> e = first; K k;
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
old = e;
break;
}
++binCount;
} while ((e = e.next) != null);
}
}
//节点存在
if (old != null) {
V v;
//根据旧值和value,得到设置的新值
if (old.value != null)
v = remappingFunction.apply(old.value, value);
else
v = value;
//新值存在
if (v != null) {
old.value = v;//设置新值
afterNodeAccess(old);
}
else//不存在则删除节点
removeNode(hash, key, null, false, true);
return v;
}
//新的值存在则添加节点
if (value != null) {
if (t != null)
t.putTreeVal(this, tab, hash, key, value);
else {
tab[i] = newNode(hash, key, value, first);
if (binCount >= TREEIFY_THRESHOLD - 1)
treeifyBin(tab, hash);
}
++modCount;
++size;
afterNodeInsertion(true);
}
return value;
}