Java List remove()
method is used to remove elements from the list. ArrayList
is the most widely used implementation of the List interface, so the examples here will use ArrayList
remove()
methods.
Version Note (2025): Examples work across modern LTS releases (Java 17, Java 21) and the upcoming Java 25 LTS (planned for September 2025). Some factory-created lists such as
List.of(...)
are immutable and will throwUnsupportedOperationException
on mutation methods includingremove(...)
. Always check whether your list is modifiable before calling mutating methods. For details, see the Oracle Java SE Support Roadmap.
remove()
MethodsThere are two remove()
methods to remove elements from the List.
E remove(int index)
: This method removes the element at the specified index and returns it. The subsequent elements are shifted to the left by one place. This method throws IndexOutOfBoundsException
if the specified index is out of range. If the list implementations does not support this operation, UnsupportedOperationException
is thrown.boolean remove(Object o)
This method removes the first occurrence of the specified Object
. If the list doesn’t contain the given element, it remains unchanged. This method returns true
if an element is removed from the list, otherwise false
. If the object is null
and list doesn’t support null
elements, NullPointerException is thrown. UnsupportedOperationException
is thrown if the list implementation doesn’t support this method.Let’s look into some examples of remove()
methods.
This example will explore E remove(int index)
:
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
list.add("C");
list.add("B");
list.add("A");
System.out.println(list);
String removedStr = list.remove(1);
System.out.println(list);
System.out.println(removedStr);
First, this code constructs and prints a list:
Output[A, B, C, C, B, A]
Then, this code executes remove(1)
to remove the element at index 1
. Finally, it prints the new resulting list and also prints the removed element.
Output[A, C, C, B, A]
B
The B
at index 1
has been removed.
IndexOutOfBoundsException
with remove(int index)
MethodThis example will explore E remove(int index)
when the index exceeds the list:
List<String> list = new ArrayList<>();
list.add("A");
String removedStr = list.remove(10);
This code constructs a list with a length of 1
. However, when the code attempts to remove the element at index 10
:
OutputException in thread "main" java.lang.IndexOutOfBoundsException: Index 10 out of bounds for length 1
at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:248)
at java.base/java.util.Objects.checkIndex(Objects.java:372)
at java.base/java.util.ArrayList.remove(ArrayList.java:535)
at com.journaldev.java.ArrayListRemove.main(ArrayListRemove.java:19)
This attempt throws the IndexOutOfBoundsException
.
remove()
UnsupportedOperationException
ExampleThe List.of()
method creates an immutable list, which can’t be modified.
List<String> list = List.of("a", "b");
System.out.println(list);
String removedStr = list.remove(1);
System.out.println(removedStr);
First, this code constructs and prints an immutable list:
Output[a, b]
Then the code attempts to use the remove()
method to remove the element at index 1
:
OutputException in thread "main" java.lang.UnsupportedOperationException
at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:142)
at java.base/java.util.ImmutableCollections$AbstractImmutableList.remove(ImmutableCollections.java:258)
at TestRemoveList.main(TestRemoveList.java:12)
This attempt throws UnsupportedOperationException
. It will also throw UnsupportedOperationException
if you attempt list.remove("a")
or list.remove("b")
.
This example will explore boolean remove(Object o)
:
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
list.add("C");
list.add("B");
list.add("A");
System.out.println(list);
boolean isRemoved = list.remove("C");
System.out.println(list);
System.out.println(isRemoved);
isRemoved = list.remove("X");
System.out.println(list);
System.out.println(isRemoved);
First, this code constructs and prints a list:
Output[A, B, C, C, B, A]
Then, this code executes remove("C")
to remove the first instance of C
. Next, it prints the resulting list and also prints the boolean value of the operation - true
:
Output[A, B, C, B, A]
true
Then, this code executes remove("X")
, but there is no instance of X
in the list, the list does not change. Finally, it prints the list and also prints the boolean value of the operation - false
:
Output[A, B, C, B, A]
false
remove()
Method in JavaIn many applications, you might need to remove specific elements from a list based on certain conditions. For example, in a social media platform, you might want to remove all posts from a user who has been banned. You can use the remove()
method to achieve this. Here’s an example:
List<Post> posts = new ArrayList<>();
// Assuming posts is populated with user posts
// Remove all posts from a banned user
String bannedUserId = "bannedUser123";
posts.removeIf(post -> post.getUserId().equals(bannedUserId));
When processing data in web or backend applications, you might need to filter out certain elements from a list based on specific criteria. The remove()
method can be used to clean up the list after filtering. For instance, in an e-commerce platform, you might want to remove all products that are out of stock from a list of products to be displayed to customers. Here’s an example:
List<Product> products = new ArrayList<>();
// Assuming products is populated with all products
// Remove all out-of-stock products from the list
products.removeIf(Product::isOutOfStock);
Before processing data, it’s essential to ensure that the data is clean and free of duplicates. The remove()
method can be used to remove duplicates from a list. For example, in a data processing pipeline, you might want to remove duplicate records from a list of data entries. Here’s an example:
List<DataEntry> dataEntries = new ArrayList<>();
// Assuming dataEntries is populated with data entries
// Remove all duplicate data entries from the list
dataEntries.removeIf(dataEntry -> dataEntries.indexOf(dataEntry) != dataEntries.lastIndexOf(dataEntry));
ConcurrentModificationException
when removing inside a for-each loopA ConcurrentModificationException
occurs when you try to modify a list (e.g., by removing an element) while iterating over it using a for-each loop. This is because the for-each loop is designed to iterate over the list in its original state, and modifying the list during iteration can lead to unpredictable behavior.
To fix this error, you can use an iterator explicitly and call its remove()
method to safely remove elements from the list while iterating. Here’s an example:
List<String> list = new ArrayList<>();
// Assuming list is populated with elements
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
if (element.equals("specificElementToRemove")) {
iterator.remove();
}
}
Alternatively, you can use a traditional for loop with an index and remove elements using the remove(int)
method. However, be careful when adjusting the index to account for the removed elements.
remove(int)
and remove(Object)
in overloaded methodsThe remove(int)
method removes the element at the specified index, while the remove(Object)
method removes the first occurrence of the specified element. If the element is not found in the list, the remove(Object)
method does not modify the list and returns false
.
To avoid confusion between these methods, ensure you understand the context and the type of parameter you are passing. If you need to remove an element by its index, use remove(int)
. If you need to remove an element by its value, use remove(Object)
.
Here is a comparison table to help you understand the differences:
Method | Parameter Type | Description | Returns |
---|---|---|---|
remove(int) |
int |
Removes the element at the specified index. | The element that was removed from the list. |
remove(Object) |
Object |
Removes the first occurrence of the specified element. | true if the list contained the specified element, false otherwise. |
Remember to use the correct method based on your requirements to avoid unexpected behavior or errors.
IndexOutOfBoundsException
errorAn IndexOutOfBoundsException
occurs when you try to access or remove an element at an index that is out of the list’s bounds. This can happen if you pass an index that is less than 0 or greater than or equal to the list’s size.
To fix this error, ensure that the index you are using is within the valid range of the list. You can do this by checking the list’s size before attempting to access or remove an element. Here’s an example:
List<String> list = new ArrayList<>();
// Assuming list is populated with elements
int indexToRemove = 5; // Example index
if (indexToRemove >= 0 && indexToRemove < list.size()) {
list.remove(indexToRemove);
} else {
System.out.println("Index is out of bounds.");
}
remove()
vs removeIf()
The remove()
method is used to remove an element from the list based on its index. The removeIf()
method is used to remove elements from the list based on a condition.
Here is a comparison table to help you understand the differences:
Method | Description | Returns | Throws |
---|---|---|---|
remove(int) |
Removes the element at the specified index. | The element that was removed from the list. | IndexOutOfBoundsException if the index is out of range (index < 0 or index >= size()). |
remove(Object) |
Removes the first occurrence of the specified element. | true if the list contained the specified element, false otherwise. |
None |
removeIf(Predicate<? super E> filter) |
Removes all elements of the list that match the given predicate. | true if any elements were removed, false otherwise. |
UnsupportedOperationException if the list implementation does not support this operation. |
CopyOnWriteArrayList
in JavaCopyOnWriteArrayList
is a thread-safe variant of ArrayList
that is designed for scenarios where reads are much more frequent than writes. It uses a “copy-on-write” strategy, meaning it creates a new copy of the underlying array whenever the list is modified.
CopyOnWriteArrayList
This collection is ideal for:
This example will explore CopyOnWriteArrayList
:
List<String> list = new CopyOnWriteArrayList<>();
list.add("A");
list.add("B");
list.add("C");
System.out.println(list);
list.remove("B");
System.out.println(list);
First, this code constructs and prints a list:
Output[A, B, C]
Then, this code executes remove("B")
to remove the element B
. Finally, it prints the resulting list:
Output[A, C]
The element B
has been removed.
remove()
in JavaWhen working with Java lists, it’s important to understand that not all List
implementations behave the same way when you remove elements. The performance and behavior of the remove()
method can vary significantly depending on the type of list you are using. This section will help you choose the right list for your needs and avoid common pitfalls.
remove()
Perform in Different List Types?Here’s a friendly breakdown of how remove()
works under the hood for the most common list implementations:
List Type | remove(int index) |
remove(Object o) |
What This Means for You |
---|---|---|---|
ArrayList |
O(n) — All elements after the removed index are shifted left by one position. | O(n) — Scans for the object, then shifts elements if found. | Fast for random access (getting by index), but removing from the start or middle can be slow for large lists. |
LinkedList |
O(n) — Must walk the list to the index, but removal itself is O(1). | O(n) — Scans for the object, removal is O(1). | Best if you need to add or remove elements frequently in the middle or at the ends of the list. |
CopyOnWriteArrayList |
O(n) — Makes a new copy of the array on every write (including remove). | O(n) — Same as above. | Thread-safe for reading, but avoid if you need to remove elements often—removal is expensive! |
Quick Tips:
ArrayList
.LinkedList
.CopyOnWriteArrayList
is a good choice.Why does this matter?
Choosing the right list type can make your code much faster and easier to maintain. For example, removing the first element from a large ArrayList
is much slower than removing it from a LinkedList
. On the other hand, if you need to access elements by index quickly, ArrayList
is usually better.
Summary Table:
ArrayList
LinkedList
CopyOnWriteArrayList
Understanding these differences will help you write more efficient and reliable Java code when working with lists!
Mutating a non-synchronized list from multiple threads can cause race conditions or ConcurrentModificationException
.
List<String> list = Collections.synchronizedList(new ArrayList<>());
synchronized (list) {
list.remove("X");
}
Prefer non-List
concurrent structures if your workload allows:
ConcurrentLinkedQueue
(queue semantics, non-indexed)ConcurrentSkipListSet
(sorted set semantics)CopyOnWriteArrayList
is safe but expensive for frequent removals; it shines when reads dominate and modifications are rare.
removeIf()
vs StreamsUse removeIf()
for in-place conditional removals without manual index management:
list.removeIf(e -> e.startsWith("temp_"));
Streams typically build a new collection instead of mutating in place:
List<String> filtered = list.stream()
.filter(e -> !e.startsWith("temp_"))
.toList(); // Java 16+
Choose removeIf()
when you must mutate the original list; choose streams when immutability or functional style is preferred.
null
ValuesArrayList
and LinkedList
accept null
; remove(null)
removes the first null
.List.of(...)
) disallow null
and all mutations and will throw on remove(...)
.List
semantics, but they can increase concurrency; prefer safe patterns (removeIf
, synchronized wrappers, or concurrent structures) when multiple threads touch the same list.remove()
, but the focus on immutable collections and records means you’ll encounter unmodifiable lists more frequently in production code.If you want to see the practical cost of removals, microbenchmark them. Below is a minimal JMH setup you can paste into a Maven project to compare ArrayList
vs LinkedList
.
Expectation:
- Removing near the front of an
ArrayList
is slower due to element shifting (O(n)).- Once a
LinkedList
node is located, unlinking is O(1); overall time is dominated by traversal (O(n) to find the position).- For bulk conditional removals,
removeIf()
often beats manual loops due to fewer bounds checks and better inlining.
<dependencies>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.37</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.37</version>
<scope>provided</scope>
</dependency>
</dependencies>
For more advanced usage, including custom benchmarks, parameterization, and profiling, consult the official OpenJDK JMH repository. There you’ll find:
If you want to explore deeper topics—such as measuring allocation rates, using profilers, or writing parameterized benchmarks for different list types and removal patterns—the JMH repository and its JMH Samples are invaluable resources.
You can also find community discussions, troubleshooting tips, and updates on new JMH releases in the repository’s issues and README.
// build.gradle.kts
dependencies {
jmh("org.openjdk.jmh:jmh-core:1.37")
jmhAnnotationProcessor("org.openjdk.jmh:jmh-generator-annprocess:1.37")
}
plugins {
// https://github.com/melix/jmh-gradle-plugin
id("me.champeau.jmh") version "0.7.2"
}
jmh {
warmupIterations.set(3)
iterations.set(5)
fork.set(2)
timeOnIteration.set(org.gradle.kotlin.dsl.support.unzipTo.tempFile().toDuration())
// Defaults are fine; override as needed
}
./gradlew jmh
# or to filter:
./gradlew jmh -PjmhInclude=ListRemoveBench
import org.openjdk.jmh.annotations.*;
import java.util.*;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 3, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Fork(2)
@State(Scope.Thread)
public class ListRemoveBench {
@Param({"1000", "10000", "100000"})
int size;
List<Integer> arrayList;
List<Integer> linkedList;
@Setup(Level.Invocation)
public void setup() {
arrayList = new ArrayList<>(size);
linkedList = new LinkedList<>();
for (int i = 0; i < size; i++) {
arrayList.add(i);
linkedList.add(i);
}
}
@Benchmark
public Integer arrayList_remove_head() {
// O(n) due to shifting
return arrayList.remove(0);
}
@Benchmark
public Integer linkedList_remove_head() {
// O(1) unlink after O(1) access to head
return linkedList.remove(0);
}
@Benchmark
public Integer arrayList_remove_middle() {
return arrayList.remove(size / 2);
}
@Benchmark
public Integer linkedList_remove_middle() {
// traversal dominates
return linkedList.remove(size / 2);
}
@Benchmark
public boolean arrayList_removeIf_even() {
// bulk conditional removal; typically faster than manual loops
return arrayList.removeIf(x -> (x & 1) == 0);
}
@Benchmark
public boolean linkedList_removeIf_even() {
return linkedList.removeIf(x -> (x & 1) == 0);
}
}
# If using Maven exec plugin or an IDE, run the JMH main:
# (Alternatively, package a fat JAR and run `java -jar`.)
mvn -q -DskipTests package
java -jar target/benchmarks.jar ListRemoveBench -wi 3 -i 5 -f 2
linkedList_remove_head
should generally beat arrayList_remove_head
.arrayList_remove_middle
may be competitive for small sizes but scales worse as size
grows.removeIf_even
avoids index bookkeeping and often shows better throughput than manual index-based loops.If you want to understand why one implementation wins, capture a short Java Flight Recorder (JFR) trace and inspect JIT inlining:
java -XX:StartFlightRecording=filename=jmh_remove.jfr,dumponexit=true,settings=profile \
-jar target/benchmarks.jar ListRemoveBench -wi 3 -i 5 -f 2
removeIf
: high churn may indicate boxing or iterator churn.ArrayList
middle removals.Run with extra flags to dump HotSpot logs:
java -XX:+UnlockDiagnosticVMOptions -XX:+LogCompilation -XX:+PrintInlining \
-jar target/benchmarks.jar ListRemoveBench -wi 3 -i 5 -f 2
Open the generated log in JITWatch and verify whether the removeIf
lambda and hot remove(...)
call sites were inlined.
removeIf
benefits from inlining and escape analysis on your JVM.The remove(int)
method removes the element at the specified index, while the remove(Object)
method removes the first occurrence of the specified element. Here’s an example:
List<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
list.add("Cherry");
// Remove by index
list.remove(1); // Removes "Banana"
System.out.println(list); // [Apple, Cherry]
// Remove by value
list.remove("Cherry"); // Removes "Cherry"
System.out.println(list); // [Apple]
You can use the clear()
method to remove all elements from a list. Here’s an example:
List<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
list.add("Cherry");
list.clear(); // Removes all elements
System.out.println(list); // []
remove()
inside a loop?Yes, you can use remove()
inside a loop, but be careful when adjusting the index to account for the removed elements. Here’s an example:
List<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
list.add("Cherry");
for (int i = 0; i < list.size(); i++) {
if (list.get(i).equals("Banana")) {
list.remove(i); // Removes "Banana"
i--; // Adjust index to account for the removed element
}
}
System.out.println(list); // [Apple, Cherry]
ConcurrentModificationException
when removing?To avoid ConcurrentModificationException
, use an Iterator
to iterate over the list and remove elements. Here’s an example:
List<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
list.add("Cherry");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
if (element.equals("Banana")) {
iterator.remove(); // Removes "Banana"
}
}
System.out.println(list); // [Apple, Cherry]
You can use the remove(Object)
method to remove an element by its value. Here’s an example:
List<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
list.add("Cherry");
list.remove("Banana"); // Removes "Banana"
System.out.println(list); // [Apple, Cherry]
remove()
not work on some lists in Java 9+?In Java 9+, some lists, such as List.of()
, are immutable and do not support the remove()
method. Attempting to use remove()
on these lists will throw an UnsupportedOperationException
. Here’s an example:
List<String> list = List.of("Apple", "Banana", "Cherry");
try {
list.remove("Banana"); // Throws UnsupportedOperationException
} catch (UnsupportedOperationException e) {
System.out.println("This list does not support remove operations.");
}
The best way to remove elements conditionally is to use the removeIf()
method, which was introduced in Java 8. Here’s an example:
List<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
list.add("Cherry");
list.removeIf(element -> element.equals("Banana")); // Removes "Banana"
System.out.println(list); // [Apple, Cherry]
Yes. A subList
is a view, not a copy. Changes to it (like remove()
) will reflect in the original list. Modifying either after structural changes can throw a ConcurrentModificationException
.
remove()
or removeIf()
inside parallel streams?No. You should avoid mutating collections inside parallel streams. It can cause race conditions and ConcurrentModificationException
. Instead, use filter()
to construct a new collection.
remove(int)
or remove(Object)
faster?It depends:
remove(int)
is O(n) due to index shifting in ArrayList
.remove(Object)
does a linear scan and then shifts.
If the index is known, prefer remove(int)
. Otherwise, remove(Object)
is more expressive but slower for large lists.You’ve now explored how Java’s List
interface supports multiple remove()
methods: by index, by value, and conditionally with removeIf()
. Along the way you’ve seen how immutability in Java 9+ factory methods, runtime exceptions (IndexOutOfBoundsException
, UnsupportedOperationException
, ConcurrentModificationException
), and overloaded method signatures can affect your code.
With the advanced sections above, you also learned about performance trade‑offs across ArrayList
, LinkedList
, and CopyOnWriteArrayList
; how concurrency changes the picture; when to prefer removeIf()
over streams; and what to expect as of modern LTS releases (Java 17, Java 21, and the upcoming Java 25). Using microbenchmarks and profiling tools such as JMH, JFR, and JITWatch, you can validate these behaviors in your own environment and build evidence‑driven performance strategies.
Recommended Reading:
References:
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
Java and Python Developer for 20+ years, Open Source Enthusiast, Founder of https://www.askpython.com/, https://www.linuxfordevices.com/, and JournalDev.com (acquired by DigitalOcean). Passionate about writing technical articles and sharing knowledge with others. Love Java, Python, Unix and related technologies. Follow my X @PankajWebDev
I help Businesses scale with AI x SEO x (authentic) Content that revives traffic and keeps leads flowing | 3,000,000+ Average monthly readers on Medium | Sr Technical Writer @ DigitalOcean | Ex-Cloud Consultant @ AMEX | Ex-Site Reliability Engineer(DevOps)@Nutanix
Building future-ready infrastructure with Linux, Cloud, and DevOps. Full Stack Developer & System Administrator @ DigitalOcean | GitHub Contributor | Passionate about Docker, PostgreSQL, and Open Source | Exploring NLP & AI-TensorFlow | Nailed over 50+ deployments across production environments.
Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.
Full documentation for every DigitalOcean product.
The Wave has everything you need to know about building a business, from raising funding to marketing your product.
Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.
New accounts only. By submitting your email you agree to our Privacy Policy
Scale up as you grow — whether you're running one virtual machine or ten thousand.
Sign up and get $200 in credit for your first 60 days with DigitalOcean.*
*This promotional offer applies to new accounts only.