zoukankan      html  css  js  c++  java
  • 转:JAVA【modCount++;】

    ArrayList、LinkedList、HashMap中都有一个字段叫modCount。这个字段的用途,在ArrayList的父类AbstractList源码中有注释,说的很清楚:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    /**
     * The number of times this list has been <i>structurally modified</i>.Structural modifications are those that change the size of the list,
     * or otherwise perturb it in such a fashion that iterations in progress may yield incorrect results.
     *
     * <p>This field is used by the iterator and list iterator implementation returned by the {@code iterator} and {@code listIterator} methods.
     * If the value of this field changes unexpectedly, the iterator (or list iterator) will throw a {@code ConcurrentModificationException} in
     * response to the {@code next}, {@code remove}, {@code previous}, {@code set} or {@code add} operations.  This provides
     * <i>fail-fast</i> behavior, rather than non-deterministic behavior in the face of concurrent modification during iteration.
     *
     * <p><b>Use of this field by subclasses is optional.</b> If a subclass wishes to provide fail-fast iterators (and list iterators), then it
     * merely has to increment this field in its {@code add(int, E)} and {@code remove(int)} methods (and any other methods that it overrides
     * that result in structural modifications to the list).  A single call to  {@code add(int, E)} or {@code remove(int)} must add no more than
     * one to this field, or the iterators (and list iterators) will throw  bogus {@code ConcurrentModificationExceptions}.  If an implementation
     * does not wish to provide fail-fast iterators, this field may be  ignored.
     */

      protected transient int modCount = 0;

    为了显示美观,对jdk原文注释进行了换行操作。
    原文大意如下:
    该字段表示list结构上被修改的次数。结构上的修改指的是那些改变了list的长度大小或者使得遍历过程中产生不正确的结果的其它方式。
    该字段被Iterator以及ListIterator的实现类所使用,如果该值被意外更改,Iterator或者ListIterator 将抛出ConcurrentModificationException异常,
    这是jdk在面对迭代遍历的时候为了避免不确定性而采取的快速失败原则。
    子类对此字段的使用是可选的,如果子类希望支持快速失败,只需要覆盖该字段相关的所有方法即可。单线程调用不能添加删除terator正在遍历的对象,
    否则将可能抛出ConcurrentModificationException异常,如果子类不希望支持快速失败,该字段可以直接忽略。
    ----------------------------------------
    例子代码:
    单线程操作,添加或者删除元素。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public void deleteTest(){
        List<String> list = new ArrayList();
        list.add("aaaaaa");
        list.add("bbbbbb");
        list.add("cccccc");
        list.add("dddddd");
        list.add("eeeeee");
     
        Iterator it = list.iterator();
        int i = 0;
        String s = null;
        while(it.hasNext()){
            if(i==2){
                it.remove();// 如果用list.remove(it.next());会报异常
            }
            System.out.println("第"+i+"个元素"+it.next());
            i++ ;
        }
        System.out.println("----------------");
        Iterator it2 = list.iterator();
        while(it2.hasNext()){
            System.out.println(it2.next());
        }
    }

      注意:第14行,如果用list.remove(it.next());会报ConcurrentModificationException异常,原因参上。

    另:注意it.remove()删除的是最近的一次it.next()获取的元素,而不是当前iterator中游标指向的元素!!
    因此,本例中i==2时,删除的其实是bbbbbb,而不是cccccc,这很容易被忽视或者误解。如果想删掉cccccc,正确操作是先调用it.next()获取到具体元素,再判断;而且由于调用了it.next(),此时游标已经指向我们期望删除的值了。想直接数数字进行删除,在这里会容易出错误。
    其实我们可以查看Iterator的源码来验证:
    1
    2
    3
    public Iterator<E> iterator() {
        return new Itr();
    }

      返回的是一个Itr对象,这个Itr是ArrayList的内部类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return   //下一个元素的游标
        int lastRet = -1// index of last element returned; -1 if no such  //上一个元素的
        int expectedModCount = modCount;  //修改计数器期望值
     
        public boolean hasNext() {
            return cursor != size;
        }
        @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();
            int i = cursor; //此时的游标,指向的是本次要遍历的对象,因为上一次已经++了,初始值为0,没有++的情况下是第一个元素
            if (i >= size)   //越界了
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;  //游标指向了下一个元素, 但 i 的值没有变
            return (E) elementData[lastRet = i];   //将 i 赋值给lastRet,取的值是方法开始时int i=cursor;中的cursor指向的值,而且最终这个游标的数值赋值给了lastRet
        }
        public void remove() {
            if (lastRet < 0)  // 如果没有next()操作就直接remove的话,lastRet=-1,会抛异常
                throw new IllegalStateException();
            checkForComodification();
            try {
                ArrayList.this.remove(lastRet); // remove之前,cursor、lastRet的值没有修改,都是上次next之后的值,因此此处的lastRet指向上次next获取的元素
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;  // 手动将ArrayList.remove()后modCount的值赋给expectedModCount,避免引起不一致
            catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
        @Override
        @SuppressWarnings("unchecked")
        public void forEachRemaining(Consumer<? super E> consumer) {
            Objects.requireNonNull(consumer);
            final int size = ArrayList.this.size;
            int i = cursor;
            if (i >= size) {
                return;
            }
            final Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length) {
                throw new ConcurrentModificationException();
            }
            while (i != size && modCount == expectedModCount) {
                consumer.accept((E) elementData[i++]);
            }
            // update once at end of iteration to reduce heap write traffic
            cursor = i;
            lastRet = i - 1;
            checkForComodification();
        }
        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }
    以上代码告诉我们,iterator.remove()实际是remove了上次next返回的元素,并且为了防止ConcurrentModificationException异常,手动修复了修改计数的期望值,而且如果没有经过next操作就直接remove的话,会因为初始的lastRet=-1而抛出IllegalStateException异常。
  • 相关阅读:
    链表--判断一个链表是否为回文结构
    矩阵--“之”字形打印矩阵
    二叉树——平衡二叉树,二叉搜索树,完全二叉树
    链表--反转单向和双向链表
    codeforces 490C. Hacking Cypher 解题报告
    codeforces 490B.Queue 解题报告
    BestCoder19 1001.Alexandra and Prime Numbers(hdu 5108) 解题报告
    codeforces 488A. Giga Tower 解题报告
    codeforces 489C.Given Length and Sum of Digits... 解题报告
    codeforces 489B. BerSU Ball 解题报告
  • 原文地址:https://www.cnblogs.com/fangyanr/p/11731579.html
Copyright © 2011-2022 走看看