Java 列表迭代器非法状态异常

Java 列表迭代器非法状态异常,java,list,iterator,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(n

有这个班

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();
        }
    }
}
公共类迭代器tuff{
私有静态最终字符串为空=”;
公共静态void main(字符串[]args){
System.out.println(“成功:”);
成功(newCollection());
System.out.println(“失败:”);
失败(newCollection());
}
私有静态无效失败(收集myCollection){
迭代器迭代器=myCollection.Iterator();
iterator.forEachRemaining(新使用者(){
公共无效接受(字符串s){
如果(s!=空)
System.out.println(“string=“+s”);
其他的
iterator.remove();
}
});
}
私有静态集合newCollection(){
集合myList=新的LinkedList();
myList.add(空);
myList.添加(“1”);
myList.添加(“2”);
myList.添加(“3”);
返回myList;
}
私有静态无效成功(收集myCollection){
迭代器迭代器=myCollection.Iterator();
while(iterator.hasNext()){
字符串s=迭代器.next();
如果(s!=空)
System.out.println(“string=“+s”);
其他的
iterator.remove();
}
}
}
它在字符串集合上迭代,删除一个特定的空字符串并打印其他字符串。成功(收集)实现工作正常

失败的一个以一个非法的状态例外而中断。但是,它能够从迭代器中获取空字符串。这表明next()一定被调用了。另外,在默认ForEachResisting实现中

default void forEachRemaining(Consumer<? super E> action) {
    Objects.requireNonNull(action);
    while (hasNext())
        action.accept(next());
}
defaultvoid forEachRemaining(消费者问题可能是-
您正在使用迭代器并同时修改它

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();
        }
    });
}
私有静态无效失败(收集myCollection){
迭代器迭代器=myCollection.Iterator();
iterator.forEachRemaining(新使用者(){
公共无效接受(字符串s){
如果(s!=空)
System.out.println(“string=“+s”);
其他的
iterator.remove();
}
});
}
您正在使用迭代器对象调用ForEachRestaining方法,并且在该方法中还将从同一迭代器中删除该对象

未来读者:这个答案不正确!

尽管提问者已经接受了这个答案作为他们问题的解决方案,但它可能[对其他人或一般人]不起作用。请参见Andreas对问题的更透彻的分析

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

这意味着迭代器本身(您从未在其上调用过
next()
)无效。即使您在进入循环之前调用了
next()
,它也会删除错误的元素,并且还可能导致
ConcurrentModificationException
,因为它的位置没有被
ForEachRestain()更新
和项删除会干扰迭代器


关于Java库的任何问题,如果无法从Javadoc中解决,请参阅参考资料。使用它。

问题在于您使用的是
链接列表
,并且它本身的缺陷实现了
foreachresaining()

:

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

提交一个bug


更新

有一个类似的问题,WARE
cursor
lastRet
在迭代过程中没有设置,因此尽管
foreachrestaining()
的javadoc没有明确说明您不能使用
Iterator.remove()
ListIterator.add()
,但当前的实现显然没有预料到您会这样做

它们甚至不会以一致的方式失败或防护,因此它们与正常的快速失败策略不一致


因此,为文档和/或fail fast逻辑提交一个bug可能更合适。

因为ForEachLeving是迭代器中的默认方法。真的不明白你在问什么……我不希望看到迭代器的
remove()
forEachRemaining
的上下文中工作,因为它可能没有在内部使用迭代器。它在
success
的情况下工作良好,因为您正在使用迭代器并删除当前元素。但是,我没有文档参考,所以我将此作为注释发布。因为
forEachRemaining()
没有指定它的迭代方式,代码中的
迭代器没有可用的理由。请参见此处:@JimGarrison是的,我意识到它很成功,因为它已经按预期使用了很长一段时间。但是foreachrestain(最后在默认实现中)确实调用next()并将结果传递给行动。有趣的问题。我喜欢没有明显答案的问题,让我去看:-)不能在答案部分发表评论。这是一个很好的观点。
使用者
方法的主体应视为单个操作。在该操作期间,迭代器的状态未定义。当使用者方法正在运行时,如果您试图调用迭代器上的其他方法,迭代器有权感到不安。在这种情况下,正常while hasNext循环的迭代应该被视为单个操作?我不确定这是否有意义。你在看什么源代码?openjdk源代码在
LinkedList$ListItr.ForEachLeving()
中不使用局部变量。看,很有趣。。。。你说得对。OP应该接受你的回答
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();
}
public E next() {
    checkForComodification();
    if (!hasNext())
        throw new NoSuchElementException();

    lastReturned = next;
    next = next.next;
    nextIndex++;
    return lastReturned.item;
}
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++;
}
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();
}