Of course! Removing elements from a List in Java is a very common task, but it's important to choose the right method to avoid errors like ConcurrentModificationException.

Here’s a comprehensive guide covering the most common ways to remove elements from a List in Java, from the most basic to the most modern and efficient.
The Quick Answer: The Most Common Ways
| Goal | Method to Use | When to Use It |
|---|---|---|
| Remove a single element by value | list.remove(Object o) |
When you have the object you want to remove and don't care about its index. ⚠️ Can be slow. |
| Remove a single element by index | list.remove(int index) |
When you know the exact position of the element to remove. Fast. |
| Remove all elements that match a condition | Java 8+: list.removeIf(Predicate) (Older): Loop backwards with list.remove(i) |
The modern, preferred way for conditional removal. The loop-backwards method is the classic, safe way for older code. |
| Remove elements from another collection | list.removeAll(Collection c) |
When you want to remove every element that is present in another collection. |
| Remove elements not in another collection | list.retainAll(Collection c) |
The opposite of removeAll. Keeps only the elements that are also in the other collection. |
Detailed Examples and Explanations
Let's use this sample list for our examples:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
List<String> fruits = new ArrayList<>(Arrays.asList("apple", "banana", "cherry", "date", "apple", "fig"));
Removing a Single Element by Value
This is the most intuitive way. You call the remove method and pass the object you want to delete.
Method: boolean remove(Object o)

- It removes the first occurrence of the specified element from the list.
- It returns
trueif the list contained the element,falseotherwise. - Performance: For
ArrayList, this is an O(n) operation. It has to scan the list from the beginning to find the element. ForLinkedList, it's also O(n) but can be faster if the element is near the beginning.
Example:
System.out.println("Original List: " + fruits);
// Remove the first "apple"
boolean wasRemoved = fruits.remove("apple");
System.out.println("Was 'apple' removed? " + wasRemoved);
System.out.println("List after first removal: " + fruits);
// Try to remove an element that doesn't exist
boolean wasRemovedAgain = fruits.remove("grape");
System.out.println("Was 'grape' removed? " + wasRemovedAgain);
System.out.println("List after trying to remove 'grape': " + fruits);
Output:
Original List: [apple, banana, cherry, date, apple, fig]
Was 'apple' removed? true
List after first removal: [banana, cherry, date, apple, fig]
Was 'grape' removed? false
List after trying to remove 'grape': [banana, cherry, date, apple, fig]
Removing a Single Element by Index
If you know the position of the element you want to remove, this is the fastest and most precise way.
Method: E remove(int index)

- It removes the element at the specified position.
- It returns the element that was removed.
- Performance: For
ArrayList, this is an O(n) operation in the worst case (if removing from the beginning), but on average it's very fast. ForLinkedList, it's an O(n) operation because it has to traverse to the index.
Example:
System.out.println("Original List: " + fruits);
// Remove the element at index 2 ("cherry")
String removedFruit = fruits.remove(2);
System.out.println("Removed fruit: " + removedFruit);
System.out.println("List after removal by index: " + fruits);
Output:
Original List: [banana, cherry, date, apple, fig]
Removed fruit: cherry
List after removal by index: [banana, date, apple, fig]
Removing Multiple Elements Based on a Condition
This is a very common requirement. Let's say you want to remove all fruits that start with the letter 'a'.
The Modern Java 8+ Way: removeIf
This is the cleanest, safest, and most recommended approach. It uses a Predicate (a function that returns true or false) to decide which elements to remove.
Method: default boolean removeIf(Predicate<? super E> filter)
- It iterates through the list and removes every element for which the
predicatereturnstrue. - It handles all the complex iteration logic internally, so you can't get a
ConcurrentModificationException.
Example:
System.out.println("Original List: " + fruits);
// Remove all fruits that start with 'a'
fruits.removeIf(fruit -> fruit.startsWith("a"));
System.out.println("List after removeIf: " + fruits);
Output:
Original List: [banana, date, apple, fig]
List after removeIf: [banana, date, fig]
The Classic (Pre-Java 8) Way: Loop Backwards
Before removeIf was introduced, the standard way to do this was to use a traditional for loop that iterates backwards. This is crucial.
Why iterate backwards? If you iterate forwards and remove an element, the list shifts all subsequent elements to the left. This causes your loop counter to skip the next element.
Example of the WRONG way (forwards loop):
// DO NOT DO THIS - it will skip elements!
for (int i = 0; i < fruits.size(); i++) {
if (fruits.get(i).startsWith("a")) {
fruits.remove(i); // After removing "apple" at index 2, "date" moves to index 2.
// The loop then increments i to 3, skipping "date".
}
}
The CORRECT way (backwards loop):
List<String> fruitsForClassicLoop = new ArrayList<>(Arrays.asList("apple", "banana", "cherry", "date", "apple", "fig"));
System.out.println("Original List for classic loop: " + fruitsForClassicLoop);
for (int i = fruitsForClassicLoop.size() - 1; i >= 0; i--) {
if (fruitsForClassicLoop.get(i).startsWith("a")) {
fruitsForClassicLoop.remove(i);
}
}
System.out.println("List after classic backwards loop: " + fruitsForClassicLoop);
Output:
Original List for classic loop: [apple, banana, cherry, date, apple, fig]
List after classic backwards loop: [banana, cherry, date, fig]
Bulk Removal with removeAll and retainAll
These methods are useful for comparing your list against another collection.
removeAll(Collection c)
Removes from your list all of its elements that are also contained in the specified collection.
Example:
List<String> fruitsToKeep = new ArrayList<>(Arrays.asList("banana", "fig"));
System.out.println("Original List: " + fruits);
System.out.println("Fruits to keep: " + fruitsToKeep);
// Remove all fruits that are NOT in fruitsToKeep
// This is equivalent to keeping only the intersection
fruits.removeAll(fruitsToKeep); // Wait, this is wrong. Let's fix it.
// Let's start over with a fresh list
List<String> freshFruits = new ArrayList<>(Arrays.asList("apple", "banana", "cherry", "date", "fig"));
List<String> fruitsToRemove = new ArrayList<>(Arrays.asList("banana", "date", "fig"));
System.out.println("\nFresh List: " + freshFruits);
System.out.println("Fruits to remove: " + fruitsToRemove);
freshFruits.removeAll(fruitsToRemove);
System.out.println("List after removeAll: " + freshFruits);
Output:
Fresh List: [apple, banana, cherry, date, fig]
Fruits to remove: [banana, date, fig]
List after removeAll: [apple, cherry]
retainAll(Collection c)
This is the opposite of removeAll. It modifies your list to only keep the elements that are also contained in the specified collection.
Example:
List<String> freshFruits2 = new ArrayList<>(Arrays.asList("apple", "banana", "cherry", "date", "fig"));
List<String> desiredFruits = new ArrayList<>(Arrays.asList("banana", "date", "fig"));
System.out.println("\nFresh List 2: " + freshFruits2);
System.out.println("Desired fruits: " + desiredFruits);
// Keep only the fruits that are in desiredFruits
freshFruits2.retainAll(desiredFruits);
System.out.println("List after retainAll: " + freshFruits2);
Output:
Fresh List 2: [apple, banana, cherry, date, fig]
Desired fruits: [banana, date, fig]
List after retainAll: [banana, date, fig]
Important: The ConcurrentModificationException
This is a common pitfall for beginners. It happens when you try to modify a list (e.g., remove an element) while you are iterating over it with an enhanced for-loop (for-each).
Why it happens: The enhanced for-loop uses an Iterator behind the scenes. When you call list.remove() directly inside the loop, you are modifying the list's structure in a way the iterator doesn't expect. The iterator detects this inconsistency and throws the exception to prevent unpredictable behavior.
WRONG CODE (will throw an exception):
List<String> riskyList = new ArrayList<>(Arrays.asList("a", "b", "c", "a"));
System.out.println("Original risky list: " + riskyList);
// This will throw java.util.ConcurrentModificationException
for (String item : riskyList) {
if (item.equals("a")) {
riskyList.remove(item); // Direct modification during iteration!
}
}
SOLUTIONS:
- Use
removeIf(Best): This is the idiomatic Java 8+ solution. - Use an Explicit Iterator (Classic): Get the iterator and use its own
remove()method.
List<String> safeList = new ArrayList<>(Arrays.asList("a", "b", "c", "a"));
System.out.println("\nOriginal safe list: " + safeList);
Iterator<String> iterator = safeList.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
if (item.equals("a")) {
iterator.remove(); // Use the iterator's remove method!
}
}
System.out.println("List after iterator removal: " + safeList);
Summary Table
| Scenario | Recommended Method | Key Point |
|---|---|---|
| Remove one item by its value | list.remove(Object o) |
Simple, but can be slow for large ArrayLists. |
| Remove one item by its position | list.remove(int index) |
Fastest way if you know the index. |
| Remove items matching a condition | list.removeIf(Predicate) |
Modern, safe, and preferred. |
| Remove items matching a condition (Java < 8) | for loop iterating backwards |
The classic, safe way. |
| Remove all items in another collection | list.removeAll(Collection c) |
Bulk removal based on another collection. |
| Keep only items in another collection | list.retainAll(Collection c) |
Bulk retention based on another collection. |
Avoid ConcurrentModificationException |
Use removeIf or an explicit Iterator |
Never modify a list while using a for-each loop on it. |
