2

It's very clear that this code is modifying a list during iteration.

public class ArrayIterator {

    public static void main(String[] args) {

        List<String> list = new LinkedList<>(Arrays.asList("A","B","C","D","E"));

        Iterator<String> it = list.iterator();
        while (it.hasNext())
        {
            list.remove(it.next());
        }

    }
}

And so we get the expected Exception

Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.LinkedList$ListItr.checkForComodification(LinkedList.java:966)
    at java.util.LinkedList$ListItr.next(LinkedList.java:888)
    at ArrayIterator.main(ArrayIterator.java:15)

Why isn't the compiler able to warn about this?

user1717259
  • 2,717
  • 6
  • 30
  • 44
  • Aren't you able to remove elements using the `#remove()` method? – Dan Grahn May 01 '15 at 16:10
  • 2
    @screenmutt OP's using `List#remove` on purpose. OP: I don't really think the compiler should be able to handle this, an edge case would be removing an item in a `Collection` that is traversed by a recursive method and the only thing you know in the method is the usage of `Collection#remove`. – Luiggi Mendoza May 01 '15 at 16:12
  • 2
    For the same reason that the compiler won't try to find provable instances of ArrayIndexOutOfBoundsException during compilation - it's just not the compiler's responsibility to deal with these semantic problems. – Nayuki May 01 '15 at 16:14
  • 2
    Because some collections allow being modified while iterating. And because it's not the compiler job to know internal implementation details of all the classes in the API. Its job is to check that the code is syntactically correct. – JB Nizet May 01 '15 at 16:14
  • You should be using `it.remove()` not `list.remove()` and no the compiler won't warn you because this is a `runtime` exception. – Edward J Beckett May 01 '15 at 16:15

3 Answers3

7

java.util.ConcurrentModificationException is a runtime exception. It may (or may not) occur. In some scenarios, you would expect a remove() to throw this exception while iterating on a collection, but depending on the number of elements and the position of the current element, the exception would not occur.

For example, the following does not throw an exception:

List<String> list = new LinkedList<String>(Arrays.asList("A", "B", "C", "D", "E"));
Iterator<String> it = list.iterator();
while (it.hasNext()) {
    String next = it.next();
    if(next.equals("D")) {
        list.remove(next);
    }
}

On the other hand, the following throws the exception:

List<String> list = new LinkedList<String>(Arrays.asList("A", "B", "C", "D", "E"));
Iterator<String> it = list.iterator();
while (it.hasNext()) {
    String next = it.next();
    if(next.equals("C")) {
        list.remove(next);
    }
}
M A
  • 71,713
  • 13
  • 134
  • 174
  • 1
    @VinceEmigh http://stackoverflow.com/questions/24980651/java-util-concurrentmodificationexception-not-thrown-when-expected and http://stackoverflow.com/questions/24556487/it-does-not-throw-exception-concurrentmodificationexception explain why. Basically it's because the iterator fails to realize an element was removed, and its `hasNext()` returns false not allowing for the exception to be thrown by `next()`. – M A May 01 '15 at 17:46
2

The compiler doesn't consider the logic of the code, only if it's legal.

For example:

public void explode(int i) {
    System.out.println(1 / (i - i)); // always divide by zero
}

This will always throw a ArithmeticException, but it compiles fine.

Your code is a less obvious example.

Bohemian
  • 412,405
  • 93
  • 575
  • 722
1

The compiler knows about the Java language. It does not know the API contracts of any of the thousands of classes that comprise the Java standard library (Except, maybe for some of the classes in the java.lang package).

Solomon Slow
  • 25,130
  • 5
  • 37
  • 57