Java foreach NoSuchElementException

时间:2015-02-18 21:12:19

标签: java foreach iterator iteration java-7

其他几天我们在这段代码中的应用程序日志中发现了一个相当尴尬的例外:

 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在这里捕获:java.util.ArrayList $ Itr.next(ArrayList.java:834)

任何提示或解释都将受到高度赞赏。

修改 可能一个好的起点是隔离UT中的行为。有什么想法吗?

1 个答案:

答案 0 :(得分:1)

好吧,我将努力总结评论中讨论的内容。谢谢大家,并随意编辑这个答案。

错误描述感觉就像并发错误,因此我们的理论是:

  

可能是另一个线程在Iterator的hasNext()next()调用之间移除了一个元素,这个元素发生在for(:)循环的幕后(感谢Steve!)。

所以,让我们测试一下我们的理论:

1)首先,list类型为ArrayList,我们提供的是ArrayList文档:

  

ArrayList未同步,因此如果您需要多线程访问,请考虑使用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),并且在任何并发修改检查(2)之前,NoSuchElementException会抛出iterator.next()
  • 只有在找到问题的根源并重现它时才能证明(UT可能是最好的选择)。在此之前,无论如何这可能是你最好的选择。

希望它可以帮助您解决问题!