Olimpiu POP
Olimpiu POP

Reputation: 5067

Java foreach NoSuchElementException

The other days we found a rather awkward exception in the logs of our application in this piece of code:

 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);
     }
 }

where LIST is

public abstract class AbstractDataSource<T, LIST extends Collection<DataSink<? super T>>>

The implementation used is ArrayList. It just throws NoSuchElementException. To be honest I don't really understand why. Whatever I tried just had the expected behavior: no iteration through the for.

The stack trace's first line:

java.util.NoSuchElementException caught here: java.util.ArrayList$Itr.next(ArrayList.java:834)

Any hint or explanation would be highly appreciated.

EDIT Probably a good starting point would be to isolate the behavior in a UT. Any thoughts on this?

Upvotes: 2

Views: 2284

Answers (1)

Filipe Borges
Filipe Borges

Reputation: 2793

Well, I will make the effort o sum up what was discussed in the comments. Thanks everyone and fell free to edit this answer.

The error description feels like a concurrency error, so our theory is:

probably another thread removed an element between the Iterator's hasNext() and next() call, that happens behind the scenes in the for(:) loop (thanks Steve!).

So, lets test our theory:

1) First of all, list type is ArrayList and from ArrayList docs we have:

ArrayList is not synchronized, so if you need multi-threaded access, consider using List l = Collections.synchronizedList(new ArrayList(...));

2) Then we check source code to find references to NoSuchElementException: no occurrence found on java.util.ArrayList.java, but five occurrences in java.util.AbstractList.java, which the most interesting is:

   // 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();
  }

Back to our theory we can say that:

  • Yes, it is possible. ArrayList is not synchronized (1) and there is a point where a NoSuchElementException is thrown by an iterator.next() before any concurrent modification check (2).
  • It can only be proved when you find the source of problem and reproduce it (a UT is probably the best option). Until that, probably this is your best bet anyway.

Hope it helps you solve your problem!

Upvotes: 2

Related Questions