列出迭代器非法状态异常

时间:2016-12-14 20:49:34

标签: java list iterator

有这个课程

public class IteratorStuff {

    private static final String EMPTY = "";

    public static void main(String[] args) {
        System.out.println("success:");
        success(newCollection());
        System.out.println("fail:");
        fail(newCollection());
    }

    private static void fail(Collection<String> myCollection) {
        Iterator<String> iterator = myCollection.iterator();
        iterator.forEachRemaining(new Consumer<String>() {
            public void accept(String s) {
                if (s != EMPTY)
                    System.out.println("string = " + s);
                else
                    iterator.remove();
            }
        });
    }

    private static Collection<String> newCollection() {
        Collection<String> myList = new LinkedList<String>();
        myList.add(EMPTY);
        myList.add("1");
        myList.add("2");
        myList.add("3");
        return myList;
    }

    private static void success(Collection<String> myCollection) {
        Iterator<String> iterator = myCollection.iterator();
        while (iterator.hasNext()) {
            String s = iterator.next();
            if (s != EMPTY)
                System.out.println("string = " + s);
            else
                iterator.remove();
        }
    }
}

迭代字符串集合并删除特定的EMPTY字符串并打印其他字符串。成功(收集)实施工作正常。

失败的一个因IllegalStateException而中断。但是,它能够从迭代器中获取EMPTY字符串。这表明必须调用next()。另外,在默认的forEachRemaining实现中

default void forEachRemaining(Consumer<? super E> action) {
    Objects.requireNonNull(action);
    while (hasNext())
        action.accept(next());
}
调用

next()以及将任何元素传递给action.accept()。在一方面,我也似乎无法找到LinkedList返回的迭代器的实现。

这是一个错误吗?如何返回第一个元素并仍然导致IllegalStateException?

此外,仅当第一个元素是EMPTY字符串时才会发生这种情况。

3 个答案:

答案 0 :(得分:2)

  

对于未来的读者:这个答案是不正确的!

     

即使提问者接受了这个答案作为他们问题的解决方案,但它可能不适用于[其他人或一般]。请参阅Andreas的this answer,以便对问题进行更全面的分析。

如果您查看GrepCode中LinkedList$ListItr的代码(ListIterator返回的LinkedList#iterator()实现),您会发现它不会更新迭代器本身,它会启动来自当前元素并使用局部变量进行迭代。

这意味着您从未调用next()的迭代器本身无效。即使您在进入循环之前调用了next(),它也会删除错误的元素,也可能导致ConcurrentModificationExceptionforEachRemaining()未更新其位置而删除项目会干扰迭代器。

<soapbox>
对于任何无法从Javadoc解析的Java库的问题,GrepCode是首选资源。使用它。
</soapbox>

答案 1 :(得分:2)

问题是您使用的是LinkedList,并且它拥有forEachRemaining()有缺陷的实现。

Source

public void forEachRemaining(Consumer<? super E> action) {
    Objects.requireNonNull(action);
    while (modCount == expectedModCount && nextIndex < size) {
        action.accept(next.item);
        lastReturned = next;
        next = next.next;
        nextIndex++;
    }
    checkForComodification();
}

使用默认实现,accept()方法将在next()返回后才会被调用。

public E next() {
    checkForComodification();
    if (!hasNext())
        throw new NoSuchElementException();

    lastReturned = next;
    next = next.next;
    nextIndex++;
    return lastReturned.item;
}

由于remove()检查lastReturned的值,因此需要在调用accept()之前设置该值。

public void remove() {
    checkForComodification();
    if (lastReturned == null)
        throw new IllegalStateException();

    Node<E> lastNext = lastReturned.next;
    unlink(lastReturned);
    if (next == lastReturned)
        next = lastNext;
    else
        nextIndex--;
    lastReturned = null;
    expectedModCount++;
}

如前所述,forEachRemaining()实施错误。它应该是:

public void forEachRemaining(Consumer<? super E> action) {
    Objects.requireNonNull(action);
    while (modCount == expectedModCount && nextIndex < size) {
        lastReturned = next;
        next = next.next;
        nextIndex++;
        action.accept(lastReturned.item);
    }
    checkForComodification();
}

提交错误!

<强>更新

ArrayList$Itr.forEachRemaining()有一个类似的问题,cursorlastRet在迭代期间没有设置,所以虽然forEachRemaining()的javadoc没有明确说你不能使用Iterator.remove()ListIterator.add(),目前的实施显然并不期望您愿意。

他们甚至不会以一致的方式失败或保护,因此他们与正常的快速失败政策不一致。

因此,提交文档和/或快速逻辑失败可能更合适。

答案 2 :(得分:1)

问题可能是 - 你正在研究迭代器并同时修改它。

private static void fail(Collection<String> myCollection) {
    Iterator<String> iterator = myCollection.iterator();
    iterator.forEachRemaining(new Consumer<String>() {
        public void accept(String s) {
            if (s != EMPTY)
                System.out.println("string = " + s);
            else
                iterator.remove();
        }
    });
}

您正在使用iterator对象调用forEachRemaining方法,并且您也在同一个迭代器中删除该对象。