Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Weird Java Concurrent modification exception example [duplicate]

Tags:

java

exception

If we write like this, there is a concurrent modification exception :

public static void main(String... args) {
    List<String> listOfBooks = new ArrayList<>();
    listOfBooks.add("Programming Pearls");
    listOfBooks.add("Clean Code");
    listOfBooks.add("Effective Java");
    listOfBooks.add("Code Complete");

    System.err.println("Before deleting : " + listOfBooks);
    for (String book : listOfBooks) {
        if (book.contains("Code")) {
            listOfBooks.remove(book);
        }
    }
    System.err.println("After deleting : " + listOfBooks);
}

On the other hand, if we write like this, there is NO concurrent modification exception ! Notice that code is exact the same, except the strings for compare, in first example it is a Code, and in second it is a Java

public static void main(String... args) {
    List<String> listOfBooks = new ArrayList<>();
    listOfBooks.add("Programming Pearls");
    listOfBooks.add("Clean Code");
    listOfBooks.add("Effective Java");
    listOfBooks.add("Code Complete");

    System.err.println("Before deleting : " + listOfBooks);
    for (String book : listOfBooks) {
        if (book.contains("Java")) {
            listOfBooks.remove(book);
        }
    }
    System.err.println("After deleting : " + listOfBooks);
}

I'm using Netbeans 8.2, Windows 7 32bit, with JDK 1.8.0_131 What's wrong ?

like image 354
dobrivoje Avatar asked Oct 07 '18 13:10

dobrivoje


2 Answers

List.remove() will not throw ConcurrentModificationException when it removes the second last element from the list.

Quoting from this Java Bug (JDK-4902078) .

When the Collections Framework was added to the platform it was deemed too expensive to check for comodification once rather than twice per iteration; the check was made on Iterator.next rather than Iterator.hasNext. Expert reviewers thought this was sufficient. They were unaware that it fails to detect one important case: if an element is removed from the list immediately prior to the final call to hasNext in an iteration, the call returns false and the iteration terminates, silently ignoring the last element on the list.

You can also check this answer :-

https://stackoverflow.com/a/8189786/1992276

like image 145
ganit44 Avatar answered Nov 15 '22 16:11

ganit44


There are two ways used to iterate over an collection: enumeration and iterator.

First one allows for the collection to be modified during iteration (fail slow), second does not (fail fast). In a for-each loop you are using an iterator, so any modification to the collection, during it's iteration would cause an exception.

You have 3 choices, to solve this problem:

Use an iterator instead:

Iterator<String> bookIt = listOfBooks.iterator();
while(bookIt.hasNext()){
   String book = bookIt.next();
   if (book.contains("Java")) {
       bookIt.remove();
   }
}

Create a new list with only acceptable elements (filter out the unwanted):

 List<String> booksWithNoCode =  listOfBooks.stream()
 .filter(book-> !book.contains("Code"))
 .collect(toList())

Use Collection.removeIf(), you will remove all elements from the list, that are matching given criteria.

listOfBooks.removeIf(book-> book.contains("Code"))

You can find more information in this post and here.

like image 35
Beri Avatar answered Nov 15 '22 17:11

Beri