Remove Element from ArrayList in Java: remove() and removeIf()

Learn to remove element from ArrayList in Java by index, value or condition with remove(), removeIf(), removeAll() and retainAll(), plus common traps.

Decision tree that starts with what we know about the element and leads to remove(int index), remove(Object), removeAll(), removeIf() or retainAll()

Removing an element from an ArrayList in Java takes one method call, remove(int index) for a position or remove(Object o) for a value, and removeIf() deletes every element that matches a condition. For whole groups of values, removeAll() deletes the values we pass, and retainAll() keeps only those values and deletes the rest.

We remove elements when a guest cancels an event booking, when a cart drops items that went out of stock, or when we clean null values out of imported data. Each method changes the list in place and shifts the remaining elements to the left, so no gaps stay behind.

The following example removes guests from an event list with each of these methods.

List<String> guests = new ArrayList<>(List.of("Alex", "Mia", "Sam", "Mia", "Zoe", "Tom"));
String first = guests.remove(0);                          // "Alex", guests = [Mia, Sam, Mia, Zoe, Tom]
boolean once = guests.remove("Mia");                      // true, guests = [Sam, Mia, Zoe, Tom]
boolean all = guests.removeAll(List.of("Mia"));           // true, guests = [Sam, Zoe, Tom]
boolean match = guests.removeIf(g -> g.startsWith("Z"));  // true, guests = [Sam, Tom]
boolean keep = guests.retainAll(List.of("Sam", "Lea"));   // true, guests = [Sam]

Notice that remove(“Mia”) deletes only the first “Mia”, whereas removeAll() deletes every copy. We pick the right method for each situation first, look at the index and value versions with the List<Integer> trap, and cover removing all occurrences, removing while iterating and the common errors.

1. Picking the Right Remove Method

The right method depends on what we know about the element. A position calls for remove(int), a value for remove(Object), and a rule such as “every guest who declined” for removeIf().

Decision tree that starts with what we know about the element and leads to remove(int index), remove(Object), removeAll(), removeIf() or retainAll()
Start from what we know about the elements to delete, and the tree points to one method of the ArrayList

All five methods are declared in the List or Collection interface, so they work on any modifiable list, not only on an ArrayList. The return value tells us what happened.

MethodRemovesReturns
remove(int index)The element at the indexThe removed element
remove(Object o)The first element equal to otrue if an element was removed
removeAll(Collection c)Every element contained in ctrue if the list changed
removeIf(Predicate p)Every element that matches ptrue if the list changed
retainAll(Collection c)Every element not contained in ctrue if the list changed
removeFirst(), removeLast()The first or last element (Java 21)The removed element

2. Removing an Element by Index with remove(int)

The method remove(int index) deletes the element at the position and returns it, so we can log it or show an undo button. Every element after the index moves one place to the left, and the size drops by one. List indexes start at 0, and an index outside 0 to size() – 1 throws an IndexOutOfBoundsException.

List<String> guests = new ArrayList<>(List.of("Alex", "Mia", "Sam", "Zoe"));
String removed = guests.remove(1);            // "Mia", guests = [Alex, Sam, Zoe]
int size = guests.size();                     // 3
String bad = guests.remove(10);               // IndexOutOfBoundsException: Index 10 out of bounds for length 3

Removing the last element is fast, because nothing has to move. Removing the first element of a big list copies every other element one place to the left, so a loop that removes from the front of a large list is slow. Java 21 added sequenced collections, which give every list the methods removeFirst() and removeLast().

List<String> waitlist = new ArrayList<>(List.of("Alex", "Mia", "Sam"));
String next = waitlist.removeFirst();         // "Alex", waitlist = [Mia, Sam]
String lastIn = waitlist.removeLast();        // "Sam", waitlist = [Mia]

List<String> empty = new ArrayList<>();
String none = empty.removeLast();             // NoSuchElementException

On an empty list, removeFirst() and removeLast() throw a NoSuchElementException, so we check isEmpty() first when the list can be empty.

3. Removing an Element by Value with remove(Object)

The method remove(Object o) searches the list from the start and deletes the first element that is equal to o, comparing with equals(). It returns true when it found and removed an element, or false when the list has no such element, and in that case the list stays unchanged.

List<String> guests = new ArrayList<>(List.of("Mia", "Sam", "Mia", "Zoe"));
boolean first = guests.remove("Mia");         // true, guests = [Sam, Mia, Zoe]
boolean second = guests.remove("Mia");        // true, guests = [Sam, Zoe]
boolean third = guests.remove("Mia");         // false, guests = [Sam, Zoe]

The search uses equals(), so objects of our own classes need a correct equals() method. A record gets one from the compiler, which compares all components.

record Guest(String name, int seat) {}
List<Guest> table = new ArrayList<>(List.of(new Guest("Mia", 4), new Guest("Sam", 7)));
boolean left = table.remove(new Guest("Sam", 7));   // true, table = [Guest[name=Mia, seat=4]]

3.1. The remove(int) vs remove(Integer) Trap

A List<Integer> has both overloads, and Java picks remove(int index) for an int argument, because it does not need autoboxing. So seats.remove(7) tries to delete the element at index 7, not the number 7. To remove by value, we pass an Integer object.

List<Integer> seats = new ArrayList<>(List.of(3, 7, 12, 1));
Integer atIndex = seats.remove(1);                    // 7 (index 1), seats = [3, 12, 1]
boolean byValue = seats.remove(Integer.valueOf(1));   // true (value 1), seats = [3, 12]
Integer wrong = seats.remove(12);                     // IndexOutOfBoundsException: Index 12 out of bounds for length 2

For a List<Integer>, remove(7) removes by index, and remove(Integer.valueOf(7)) removes by value. The cast remove((Integer) 7) works too. The same trap hits a List<Long> called with an int literal such as remove(7), and a List<Short> called with a short variable, because Java widens a short to int before it tries boxing.

4. Removing All Occurrences of an Element

The method remove(Object) deletes one copy per call. To delete every copy of a value in one pass, we call removeIf() with an equality check, or removeAll() with a collection that holds only that value. Both scan the list once and compact it in a single step.

List<String> guests = new ArrayList<>(List.of("Mia", "Sam", "Mia", "Zoe", "Mia"));
boolean viaIf = guests.removeIf("Mia"::equals);                     // true, guests = [Sam, Zoe]

List<String> again = new ArrayList<>(List.of("Mia", "Sam", "Mia"));
boolean viaAll = again.removeAll(Collections.singleton("Mia"));     // true, again = [Sam]

When the value can be null, we write removeIf(g -> Objects.equals(g, value)), because value::equals throws a NullPointerException for a null value. To keep the original list and get a new one without the value, we filter a stream. The method toList() returns an unmodifiable list.

List<String> guests = List.of("Mia", "Sam", "Mia", "Zoe");
List<String> withoutMia = guests.stream().filter(g -> !g.equals("Mia")).toList();   // [Sam, Zoe]

A for loop that calls remove(i) while counting up skips elements. After a removal, the next element moves into position i, and the loop moves on to i + 1 without checking it, so two equal neighbors leave one copy behind.

static void removeForward(List<String> list, String value) {
    for (int i = 0; i < list.size(); i++) {
        if (list.get(i).equals(value)) {
            list.remove(i);                   // the next element moves into i and is skipped
        }
    }
}
List<String> names = new ArrayList<>(List.of("Mia", "Mia", "Sam"));
removeForward(names, "Mia");
String result = names.toString();             // "[Mia, Sam]"

Counting down from the last index avoids the skip, but each remove(i) still shifts the tail of the list. The method removeIf() moves every kept element once, so it stays fast for large lists with many matches.

5. Removing a Group of Values with removeAll()

The method removeAll(Collection c) deletes every element of the list that the collection c contains, including all duplicates. It returns true when it removed at least one element. For each element of the list, it calls c.contains(), so the type of c decides the speed.

List<String> guests = new ArrayList<>(List.of("Alex", "Mia", "Sam", "Mia", "Zoe"));
boolean changed = guests.removeAll(List.of("Mia", "Zoe", "Lea"));   // true, guests = [Alex, Sam]
boolean same = guests.removeAll(List.of("Lea"));                     // false, guests = [Alex, Sam]
boolean nullArg = guests.removeAll(null);                            // NullPointerException

When both lists are large, we pass a HashSet, as in guests.removeAll(new HashSet<>(cancelled)). A List argument scans all of its elements for every element of our list, whereas a HashSet answers contains() in constant time on average.

6. Removing Elements by Condition with removeIf()

The method removeIf(Predicate filter), added in Java 8, deletes every element for which the Predicate returns true. We use it whenever the rule is easier to write than the list of values, such as “names that start with Z” or “seats above 100”. It throws a NullPointerException when the predicate is null.

List<Integer> seats = new ArrayList<>(List.of(4, 15, 8, 23, 42));
boolean odd = seats.removeIf(s -> s % 2 != 0);        // true, seats = [4, 8, 42]

List<String> imported = new ArrayList<>(Arrays.asList("Mia", null, "Sam", null));
boolean cleaned = imported.removeIf(Objects::isNull); // true, imported = [Mia, Sam]

For objects, the predicate reads a field. Predicates also combine with and(), or() and negate(), so a rule built from smaller rules stays readable.

record Rsvp(String name, boolean attending, int plusOnes) {}
List<Rsvp> replies = new ArrayList<>(List.of(new Rsvp("Mia", true, 1), new Rsvp("Sam", false, 0), new Rsvp("Zoe", true, 3)));
Predicate<Rsvp> declined = r -> !r.attending();
Predicate<Rsvp> tooMany = r -> r.plusOnes() > 2;
boolean dropped = replies.removeIf(declined.or(tooMany));   // true, replies = [Rsvp[name=Mia, attending=true, plusOnes=1]]

7. Keeping Only Some Elements with retainAll()

The method retainAll(Collection c) is the opposite of removeAll(). It keeps the elements that c contains and deletes all others, so the list becomes the intersection of both collections, in the original order of our list. The argument collection does not change.

List<String> invited = new ArrayList<>(List.of("Alex", "Mia", "Sam", "Zoe"));
List<String> checkedIn = List.of("Zoe", "Mia", "Lea");
boolean changed = invited.retainAll(checkedIn);       // true, invited = [Mia, Zoe]
boolean again = invited.retainAll(checkedIn);         // false, invited = [Mia, Zoe]

Passing an empty collection removes every element, the same as clear(). Like removeAll(), the method calls contains() on the argument, so a HashSet argument is faster for large lists.

8. Removing Elements While Iterating

Calling list.remove() inside a for-each loop changes the list behind the back of the loop’s iterator. The iterator notices the change on its next step and throws a ConcurrentModificationException, which is called fail-fast behavior.

static void dropAlexInLoop(List<String> guests) {
    for (String g : guests) {
        if (g.equals("Alex")) {
            guests.remove(g);
        }
    }
}
List<String> guests = new ArrayList<>(List.of("Alex", "Mia", "Sam"));
dropAlexInLoop(guests);                       // ConcurrentModificationException

When we need the loop anyway, for example to log each removed guest, we use the remove() method of an explicit Iterator. It deletes the element that next() returned last and keeps the iterator in sync. In every other case, removeIf() does the same job in one line.

List<String> guests = new ArrayList<>(List.of("Alex", "Mia", "Sam", "Ann"));
Iterator<String> it = guests.iterator();
while (it.hasNext()) {
    String g = it.next();
    if (g.startsWith("A")) {
        it.remove();
    }
}
String result = guests.toString();            // "[Mia, Sam]"

9. Dropping Unpaid Bookings in a Ticket App

Say an event ticketing app keeps today’s bookings in a list. A booking that is still unpaid 15 minutes after it was made must go, so the seat returns to sale, and the app shows how many seats it released. A single removeIf() call does the work, and the size difference gives the count.

record Booking(String guest, int seat, boolean paid, int minutesOld) {}
static int releaseUnpaid(List<Booking> bookings) {
    int before = bookings.size();
    bookings.removeIf(b -> !b.paid() && b.minutesOld() > 15);
    return before - bookings.size();
}
List<Booking> bookings = new ArrayList<>(List.of(new Booking("Mia", 4, true, 30), new Booking("Sam", 7, false, 20), new Booking("Zoe", 9, false, 5)));
int released = releaseUnpaid(bookings);       // 1, Sam's seat 7 is free again
int stillHeld = bookings.size();              // 2

Zoe’s booking stays, because it is unpaid but only 5 minutes old. Writing the same logic with an index loop would need the backward loop from section 4, and the condition would be harder to read.

10. ArrayList Remove FAQs

Most follow-up questions about removing elements are about speed, capacity and lists that refuse changes.

10.1. Why Does remove() Throw UnsupportedOperationException?

The list cannot change its size. Lists from List.of(), Stream.toList() and Collections.unmodifiableList() reject every change, and the list from Arrays.asList() has a fixed size. We copy the list into new ArrayList<>(list) first, as the UnsupportedOperationException article explains.

List<String> fixed = Arrays.asList("Mia", "Sam");
boolean failed = fixed.remove("Mia");                         // UnsupportedOperationException
List<String> copy = new ArrayList<>(fixed);
boolean worked = copy.remove("Mia");                          // true, copy = [Sam]

10.2. What Is the Time Complexity of ArrayList remove()?

Removing by index costs time in proportion to the number of elements after the index, because they all shift left, so removing the last element is constant time. Removing by value adds a linear search, so remove(Object) is linear in the list size. For frequent removals at the front, an ArrayDeque is a better fit.

10.3. Does remove() Shrink the Capacity of an ArrayList?

No. The size drops, but the backing array keeps its length, so the memory stays reserved. After removing most elements of a large list that lives for a long time, we call trimToSize() to shrink the array to the current size.

10.4. How Do We Remove Duplicates from an ArrayList?

We copy the list into a LinkedHashSet, which drops duplicates and keeps the first occurrence of each element in order, or use stream().distinct().toList(). The remove duplicates from ArrayList article compares both.

List<String> names = List.of("Mia", "Sam", "Mia", "Zoe");
List<String> unique = new ArrayList<>(new LinkedHashSet<>(names));   // [Mia, Sam, Zoe]

10.5. Should We Use clear() or removeAll() to Empty a List?

We call clear(). It sets every slot to null in one pass, whereas list.removeAll(list) checks every element against the argument and does more work for the same result.

11. Conclusion

To remove an element from an ArrayList, we call remove(int) for a position or remove(Object) for the first equal value. For a List<Integer>, we wrap the value in Integer.valueOf(), otherwise Java removes by index.

For many elements, removeIf() deletes everything that matches a condition, removeAll() deletes every copy of the given values, and retainAll() keeps only them. Inside a loop, we remove through the iterator, or better, replace the loop with removeIf().

12. References

Happy Learning !!

Source Code on Github

Leave a Comment

  1. How about if the array has like below:

    ArrayList namesList = new ArrayList(Arrays.asList( “alex(HR)”, “brian(employee)”, “charles(CEO)”, “alex”) )

    How to remove the brackets including contents inside, i want to show only the names?

Comments are closed.

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.