对比1.7的优化
- 取消了indexFor()方法,直接使用table[index = (n-1)&hash]
- 增加了TreeNode,当链表个数达到8个以上(不包含8个)就会转换成红黑树结构,提升了查找策略,选择8是因为泊松分布,可以看代码的注释即可,留出7,是留了一个缓冲空间,避免链表转红黑树,红黑树转链表的频繁发生
- 优化了扩容后的index计算,比如扩容前是16,扩容后是32,那么0000 1111的hash位置不变,而0001 1111只需要在原来的hash位置+16即可
- 使用Node代替了Entry
多线程并发操作丢失数据
测试代码
public class TestHashMap {
static Map map = new HashMap(2);
public static void main(String[] args) throws Exception {
for (int i = 0; i<100; i++) {
Thread thread = new Thread(new Run(i));
thread.start();
}
TimeUnit.SECONDS.sleep(1);
System.out.println(map.size());
}
static class Run implements Runnable{
int i;
public Run( int i) {
this.i=i;
}
@Override
public void run() {
for (int j = 0; j <10; j++) {
map.put(Thread.currentThread().getName() + " j="+j,i);
}
}
}
}
原理:
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
if (oldTab != null) {//原来数组有值,进行扩容操作
for (int j = 0; j < oldCap; ++j) {//2个线程同时执行
Node<K,V> e;
//从头开始,把当前下标位置置位null
if ((e = oldTab[j]) != null) {
//线程1执行到这里,判断不为null,就把这个值设定为null,线程2执行到这里的时候判断该位置为null,就执行了j+1下标操作,并且线程2先执行完成扩容,那么原来在j位置的数据就丢失了
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;
}
}
}
}
如何解决1.7出现的环形链表问题
看关键代码
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;
}
//原索引+oldCap
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
// 原索引放到bucket里
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
// 原索引+oldCap放到bucket里
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
如何解决的死循环
2对指针,依次在末端添加新的元素
主要就是不在原来的扩容后的数组上直接放置元素,而是构建成功链表后再放入
多个线程的话,也就是多个线程执行同样的操作而已
loHead -> loTail
hiHead -> hiTail