为什么HashMap迭代器是快速失败的?如何实现的?

HashMap table = new HashMap();
table.put("a", "aa");
table.put("b", "dd");
table.put("c", "cc");
Iterator> iterator = table.entrySet().iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next().getValue());
    //1.使用iterator的方法操作元素,可以正常运行
    iterator.remove();
    //2.使用集合的方法操作元素,会报ConcurrentModificationException异常
    table.put("d", "dd");
    table.remove("c");
}

    运行完这段代码,打印出了“aa”,并报ConcurrentModificationException异常,出错位置是System.out.println(iterator.next().getValue());
    紧接着,笔者去看一下iterator.next()的源码。
为什么HashMap迭代器是快速失败的?如何实现的?_第1张图片
为什么HashMap迭代器是快速失败的?如何实现的?_第2张图片
为什么HashMap迭代器是快速失败的?如何实现的?_第3张图片

    看完HashMap的源码,不难发现,entrySet的迭代器是EntryIterator ,继承了HashIterator类。调用iterator.next()方法,实际上是调用了HashIterator的nextNode方法,而报错的原因是modCount和expectedModCount不相等。
    因此,快速失败的机制是跟这两个属性有关的,modCount和expectedModCount。modCount,用于记录HashMap元素修改的次数;expectedModCount,会在迭代器初始化时被赋值为modCount。
    接下来,笔者继续通过阅读源码对比一下集合的remove方法和迭代器的remove方法,找出是什么原因导致前者快速失败,而后者不会。
    使用集合的方法(put、remover等)操作元素时,会修改modCount值,但不会同步更新expectedModCount,代码如下:

public V remove(Object key) {
        Node e;
        return (e = removeNode(hash(key), key, null, false, true)) == null ?
            null : e.value;
    }
final Node removeNode(int hash, Object key, Object value,
                               boolean matchValue, boolean movable) {
        Node[] tab; Node p; int n, index;
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (p = tab[index = (n - 1) & hash]) != null) {
            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 TreeNode)
                    node = ((TreeNode)p).getTreeNode(hash, key);
                else {
                    do {
                        if (e.hash == hash &&
                            ((k = e.key) == key ||
                             (key != null && key.equals(k)))) {
                            node = e;
                            break;
                        }
                        p = e;
                    } while ((e = e.next) != null);
                }
            }
            if (node != null && (!matchValue || (v = node.value) == value ||
                                 (value != null && value.equals(v)))) {
                if (node instanceof TreeNode)
                    ((TreeNode)node).removeTreeNode(this, tab, movable);
                else if (node == p)
                    tab[index] = node.next;
                else
                    p.next = node.next;
                ++modCount;    //记录HashMap修改次数
                --size;
                afterNodeRemoval(node);
                return node;
            }
        }
        return null;
    }

    而使用迭代器的remover方法时,不止修改了modCount,还同步更新expectedModCount,代码如下:
为什么HashMap迭代器是快速失败的?如何实现的?_第4张图片
总结:
    因为迭代器的remove方法不仅修改了modCount,还同步更新了expectedModCount,而集合的remove方法只修改了modCount,不会同步expectedModCount,所以后者在调用iterator.next()时会快速失败,而前者不会。(前提是在迭代器遍历集合过程中新增、修改或删除了元素)

你可能感兴趣的:(追本溯源)