有这个课程
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字符串时才会发生这种情况。
答案 0 :(得分:2)
对于未来的读者:这个答案是不正确的!
即使提问者接受了这个答案作为他们问题的解决方案,但它可能不适用于[其他人或一般]。请参阅Andreas的this answer,以便对问题进行更全面的分析。
如果您查看GrepCode中LinkedList$ListItr
的代码(ListIterator
返回的LinkedList#iterator()
实现),您会发现它不会更新迭代器本身,它会启动来自当前元素并使用局部变量进行迭代。
这意味着您从未调用next()
的迭代器本身无效。即使您在进入循环之前调用了next()
,它也会删除错误的元素,也可能导致ConcurrentModificationException
因forEachRemaining()
未更新其位置而删除项目会干扰迭代器。
<soapbox>
对于任何无法从Javadoc解析的Java库的问题,GrepCode是首选资源。使用它。
</soapbox>
答案 1 :(得分:2)
问题是您使用的是LinkedList
,并且它拥有forEachRemaining()
的有缺陷的实现。
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()
有一个类似的问题,cursor
和lastRet
在迭代期间没有设置,所以虽然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方法,并且您也在同一个迭代器中删除该对象。