Removing name if case matches from ArrayList not working

Viewed 126

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?

5 Answers

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

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

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

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

}

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.

Related