前几天,我们在应用程序日志中的这段代码中发现了一个相当尴尬的异常:
final LIST currentSinks = this.sinks;
if (null != currentSinks && !currentSinks.isEmpty())
{
for (final DataSink<? super T> sink : currentSinks)// **the exception is thrown here**
{
sink.updated(oldValue, newValue);
}
}
其中LIST在
public abstract class AbstractDataSource<T, LIST extends Collection<DataSink<? super T>>>
使用的实现是ArrayList。它只是抛出 NoSuchElementException 。说实话我不太明白为什么。无论我尝试什么,都得到了预期的行为:没有通过 for 进行迭代。
堆栈跟踪的第一行:
java.util.NoSuchElementException caught here: java.util.ArrayList$Itr.next(ArrayList.java:834)
任何提示或解释将不胜感激。
编辑 也许一个好的起点是隔离 UT 中的行为。对此有什么想法吗?
最佳答案
好吧,我会努力总结评论中讨论的内容。谢谢大家,并自由编辑这个答案。
错误描述感觉像是并发错误,所以我们的理论是:
probably another thread removed an element between the Iterator's
hasNext()
andnext()
call, that happens behind the scenes in thefor(:)
loop (thanks Steve!).
那么,让我们测试一下我们的理论:
1) 首先,list
类型是 ArrayList
,从 ArrayList 文档我们可以得到:
ArrayList is not synchronized, so if you need multi-threaded access, consider using
List l = Collections.synchronizedList(new ArrayList(...));
2) 然后我们检查源代码以查找对 NoSuchElementException
的引用:在 java.util.ArrayList.java
上未发现任何情况,但 java.util.AbstractList.java
中出现了五次,其中最有趣的是:
// under: public ListIterator<E> listIterator(final int index) {
// return new ListIterator<E>() {
// line 1040:
/**
* Retrieves the next object from the list.
*
* @return The next object.
* @throws NoSuchElementException if there are no
* more objects to retrieve.
* @throws ConcurrentModificationException if the
* list has been modified elsewhere.
*/
public E next()
{
if (position == size)
throw new NoSuchElementException();
position++;
return i.next();
}
回到我们的理论,我们可以说:
- 是的,这是可能的。
ArrayList
未同步 (1),并且在任何并发修改检查之前,iterator.next()
会在某个时刻抛出NoSuchElementException
(2). - 只有找到问题根源并重现它时才能证明这一点(UT 可能是最好的选择)。在那之前,这可能是您最好的选择。
希望能帮助您解决问题!
关于Java foreach NoSuchElementException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28594002/