2

I am trying to write a program that will remove names from an ArrayList if the name has specific value, but I am unable to do this.

class UserNamesTest {
    public static void main(String[] args) {
        ArrayList<String> names = new ArrayList<>();
        names.add("John");
        names.add("Jerry");
        names.add("Jim");
        names.add("Todd");

        for(String name : names) {
            if(name.equals("John")) {
                System.out.println("true");
                names.remove(names.indexOf(name));
            }
        }
    }
}

When I execute this code, I am getting an exception:

Exception in thread "main" java.util.ConcurrentModificationException
        at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
        at java.util.ArrayList$Itr.next(Unknown Source)
        at UserNamesTest.main(UserNamesTest.java:13)

How can I accomplish this?

Andronicus
  • 25,419
  • 17
  • 47
  • 88
  • 4
    Does this answer your question? [Iterating through a Collection, avoiding ConcurrentModificationException when removing objects in a loop](https://stackoverflow.com/questions/223918/iterating-through-a-collection-avoiding-concurrentmodificationexception-when-re) – OH GOD SPIDERS Mar 20 '20 at 15:17
  • [Use this method](https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html#removeIf-java.util.function.Predicate-) to avoid having to iterate within your own code. – Andrew S Mar 20 '20 at 15:21

5 Answers5

3

Use the iterator not to operate on List you're iterating over:

Iterator<String> iterator = names.iterator();
while(iterator.hasNext()) {
    String name iterator.next();
    if(name.equals("John")) {
        System.out.println("true");
        iterator.remove();
    }
}
Andronicus
  • 25,419
  • 17
  • 47
  • 88
0

Because this exception may be thrown by methods that have detected concurrent modification of an object when such modification is not permissible java doc. Use removeIf over your list.

        ArrayList<String> names = new ArrayList<>();
        names.add("John");
        names.add("Jerry");
        names.add("Jim");
        names.add("Todd");
        names.removeIf(name->name.equals("John"));

Or Simply use Iterator

        Iterator<String> iter = names.iterator();
        while (iter.hasNext()) {
            String str = iter.next();
            if (str.equals("John"))
                iter.remove();
        }

Note that Iterator.remove() is the only safe way to modify a collection during iteration.more about iterator

manikant gautam
  • 3,521
  • 1
  • 17
  • 27
0

Apart from using Iterator, with java-8, you could do as below,

names = names.stream()
             .collect(Collectors.partitioningBy(e->e.equals("John")))
             .get(false);

Or more readable with filter,

names = names.stream()
             .filter(e->e.equals("John"))
             .collect(Collectors.toList());
Vikas
  • 6,868
  • 4
  • 27
  • 41
  • 1
    I'd rather use filter, this way is unreadable. Besides there is an overhead in creating the map holding list of elements that we want and the rest. – Andronicus Mar 20 '20 at 15:26
0

Using Java8 or 11 you could:

class UserNamesTest {
    public static void main(String[] args) {
        ArrayList<String> names = new ArrayList<>();
        names.add("John");
        names.add("Jerry");
        names.add("Jim");
        names.add("Todd");

        List<String> namesWithoutJohn = names.stream()
                .filter(((Predicate<String>)("John"::equals)).negate()) // Java 8
//                .filter(Predicate.not("John"::equals)) // Java 11
                .collect(Collectors.toList());
    }

}
Ezequiel
  • 3,477
  • 1
  • 20
  • 28
0

Be careful when iterating over a list using an index to remove an item. Because items get shifted after the move, you can miss some of them. Here is an example:

       ArrayList<String> names = new ArrayList<>();
       names.add("Jerry");
       names.add("Jim");
       names.add("John");
       names.add("John");
       names.add("Todd");

       for(int i = 0; i < names.size(); i++) {
           String name = names.get(i);
           if(name.equals("John")) {
               names.remove(i);
           }
       }

       names.forEach(System.out::println);   

Prints

Jerry
Jim
John  <-- wasn't removed
Todd

If you are going to to it that way, start with the last item since removing one doesn't affect subsequent positions.

       names = new ArrayList<>();
       names.add("Jerry");
       names.add("Jim");
       names.add("John");
       names.add("John");
       names.add("Todd");

       for (int i = names.size() -1; i >= 0; i--) {
           String name = names.get(i);
           if(name.equals("John")) {
               names.remove(i);
           }
       }

       names.forEach(System.out::println);   

Prints

Jerry
Jim
Todd

But my preferred way would be using the following:

names.removeIf(name->name.equals("John"));

It gets all of them with one statement.

WJS
  • 36,363
  • 4
  • 24
  • 39