Java ArrayList Tutorial: Every Method with Examples (2026)
Last Updated: | Author: Deep Singh
ArrayList is the workhorse of the Java Collections Framework. Need a list that grows as you add items? Use an ArrayList. Need random access by index? ArrayList. Need to sort, filter, search, or stream over a collection? ArrayList. It's the class you'll reach for 90% of the time when you need a list in Java.
In this tutorial, you'll get a walkthrough of every important ArrayList method — add, remove, get, set, iterate, sublist, stream operations, and more. Each one has a runnable example. By the end, you'll have a complete mental map of what ArrayList can do.
Video Walkthrough
If you prefer learning by watching, here's a video walkthrough that covers the same material:
Creating an ArrayList
Use a generic type parameter — like <String> — to tell the compiler what kind of items the list holds. This gives you type safety: no casts, no ClassCastException at runtime.
ArrayList<String> list = new ArrayList<>();
The empty diamond <> on the right side is the "diamond operator" — Java infers the type from the left side. This is the modern style since Java 7.
Adding Items
Append to the End
boolean first = list.add("suresh"); // returns true if added
boolean second = list.add("ashok");
The return value is always true for ArrayList, but the method signature returns boolean for compatibility with other collections like Set, which can reject duplicates.
Insert at a Specific Position
list.add(1, "harry"); // inserts at index 1, shifts others to the right
If the index is larger than the size, this throws IndexOutOfBoundsException. Note that list.add(list.size(), item) is a valid way to append, but plain list.add(item) is the idiomatic choice.
Iterating Over Items
Iterator (Basic)
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
System.out.println(item);
}
Because the collection is generic, iterator.next() returns a String directly — no casting needed. That's the whole point of generics.
ListIterator (Bidirectional)
ListIterator<String> listIterator = list.listIterator();
// Forward
while (listIterator.hasNext()) {
System.out.println(listIterator.next());
}
// Backward (must have iterated to the end first)
while (listIterator.hasPrevious()) {
System.out.println(listIterator.previous());
}
ListIterator is a richer version of Iterator that also lets you move backward and replace or insert elements during iteration.
For-Each Loop (Simplest)
for (String item : list) {
System.out.println(item);
}
The for-each loop is what you'll use 95% of the time. It's the cleanest way to iterate.
Lambda forEach (Java 8+)
list.forEach(item -> System.out.println(item));
This is the functional style, introduced in Java 8. It's elegant but a bit slower than a plain for-each loop due to the extra lambda indirection. For most code, the difference doesn't matter.
Removing Items
Remove by Index
String removed = list.remove(0); // removes and returns the item at index 0
Remove by Value
boolean status = list.remove("harry"); // returns true if the item was found and removed
Watch out for the overload trap: list.remove(2) removes by index (the item at position 2), while list.remove(Integer.valueOf(2)) removes by value (the item equal to 2). This is a classic source of bugs when working with ArrayList<Integer>.
Remove All Items
list.clear();
Accessing Items
Get by Index
String item = list.get(0); // returns the item at index 0
Get a Sublist
List<String> sub = list.subList(0, 2); // from index 0 (inclusive) to 2 (exclusive)
Important: subList returns a view of the original list, not a copy. Modifying the sublist modifies the original, and vice versa. If you want an independent copy, use new ArrayList<>(list.subList(0, 2)).
Replace by Index
String replaced = list.set(0, "ambuj"); // replaces item at index 0, returns the old item
Size
int size = list.size();
Check if Empty
boolean empty = list.isEmpty();
Use isEmpty() instead of size() == 0 — it's cleaner and slightly more efficient.
Searching and Checking
Check if an Item Exists
boolean exists = list.contains("suresh");
Find First Occurrence
int index = list.indexOf("suresh"); // returns -1 if not found
Find Last Occurrence
int lastIndex = list.lastIndexOf("suresh");
Both methods return -1 when the item isn't present. Always check for that before using the result as an index.
Converting and Combining
Convert to Array
Object[] items = list.toArray();
For a typed array, pass a zero-length array of the right type:
String[] items = list.toArray(new String[0]);
The new String[0] is a small idiom — the JVM uses it to know the target type and (in modern JVMs) it's actually faster than allocating the right size upfront.
Add All Items from Another Collection
ArrayList<String> another = new ArrayList<>();
another.add("nitish");
another.add("punit");
boolean changed = list.addAll(another);
Add All Items at a Specific Position
list.addAll(0, another); // inserts all items from "another" starting at index 0
Remove All Items in Another Collection
boolean changed = list.removeAll(another);
Keep Only Items That Are in Another Collection
boolean changed = list.retainAll(another);
This is the set-intersection operation. After this call, list contains only the items that were also in another.
Streams and Lambda Expressions
Java 8 introduced the Stream API, which lets you process collections with a functional style. The main methods are filter, map, limit, collect, and count.
Iterate with forEach
list.forEach(item -> System.out.println(item));
Filter and Find Any Match
Optional<String> data = list.stream()
.filter(x -> x.equals("suresh"))
.findAny();
if (data.isPresent()) {
System.out.println("exists");
} else {
System.out.println("not exists");
}
Filter, Limit, and Count
long count = list.stream()
.filter(x -> x.length() > 3)
.limit(2)
.count();
System.out.println(count);
Collect Filtered Items into a New List
List<String> filtered = list.stream()
.filter(x -> x.length() > 0)
.collect(Collectors.toList());
filtered.forEach(x -> System.out.println(x));
Parallel Stream
List<String> matches = list.parallelStream()
.filter(x -> x.contains("suresh"))
.collect(Collectors.toList());
System.out.println(matches.size());
parallelStream() splits the work across multiple CPU cores. It's faster for large collections with expensive operations — but for small lists (anything under a few thousand items), the overhead of splitting and merging can make it slower than a sequential stream. Measure before optimising.
Full Runnable Example
Here's the complete Test.java covering every method in this tutorial. You can copy it into any Java project and run it directly.
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Optional;
import java.util.stream.Collectors;
public class Test {
public static void main(String[] args) {
// --- Create ---
ArrayList<String> list = new ArrayList<>();
// --- Add ---
list.add("suresh");
list.add("ashok");
list.add(1, "harry");
// --- Iterate with Iterator ---
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
System.out.println(item);
}
// --- Remove by index and by value ---
String removed = list.remove(0);
boolean wasRemoved = list.remove("harry");
// --- Access ---
String firstItem = list.get(0);
String replaced = list.set(0, "ambuj");
List<String> subItems = list.subList(0, Math.min(2, list.size()));
// --- Search ---
int index = list.indexOf("suresh");
int lastIndex = list.lastIndexOf("suresh");
boolean exists = list.contains("suresh");
// --- Size and emptiness ---
int size = list.size();
boolean empty = list.isEmpty();
// --- Convert to array ---
String[] items = list.toArray(new String[0]);
// --- Combine with another collection ---
ArrayList<String> another = new ArrayList<>();
another.add("nitish");
another.add("punit");
list.addAll(another);
list.addAll(0, another);
list.removeAll(another);
list.retainAll(another);
// --- Lambda iteration ---
list.forEach(x -> System.out.println(x));
// --- Filter with stream ---
Optional<String> data = list.stream()
.filter(x -> x.equals("suresh"))
.findAny();
if (data.isPresent()) {
System.out.println("exists");
} else {
System.out.println("not exists");
}
// --- Filter, limit, count ---
long count = list.stream()
.filter(x -> x.length() > 3)
.limit(2)
.count();
System.out.println(count);
// --- Collect filtered into a new list ---
List<String> filtered = list.stream()
.filter(x -> x.length() > 0)
.collect(Collectors.toList());
filtered.forEach(x -> System.out.println(x));
// --- Parallel stream ---
List<String> matches = list.parallelStream()
.filter(x -> x.contains("suresh"))
.collect(Collectors.toList());
System.out.println(matches.size());
// --- Clear ---
list.clear();
System.out.println("Final size: " + list.size());
}
}
A word on this code: The original tutorial mixed every method into one file without checking intermediate results. If you actually run the original code, it throws IndexOutOfBoundsException because the list is emptied by clear() before later operations that need items. The version above keeps the operations ordered so the list is never empty when a method needs items. This is the kind of detail that only shows up when you actually run the code.
Common Pitfalls
remove(int)vsremove(Object).list.remove(2)removes by index.list.remove(Integer.valueOf(2))removes by value. This trips up almost everyone working withArrayList<Integer>.- Assuming
subListreturns a copy. It returns a view. Modifying either the sublist or the original list affects the other. Usenew ArrayList<>(list.subList(...))for an independent copy. - Modifying the list while iterating. Calling
list.add()orlist.remove()inside anIteratorloop throwsConcurrentModificationException. Use the iterator's ownremove()method, or collect changes and apply them after the loop. - Using
size() == 0instead ofisEmpty(). Both work, butisEmpty()is clearer and communicates intent better. - Not checking
indexOf()for -1. If the item isn't found,indexOfreturns -1. Using that as an index toget()orremove()throwsIndexOutOfBoundsException. - Using
parallelStream()for small lists. The overhead of splitting the work usually makes it slower for anything under a few thousand elements. Usestream()by default. - Assuming
ArrayListis thread-safe. It isn't. If multiple threads will access the list, useCollections.synchronizedList()or a concurrent collection likeCopyOnWriteArrayList. - Forgetting to specify the generic type. Raw
ArrayList list = new ArrayList();compiles with warnings and requires explicit casts on everyget(). Always useArrayList<T>. - Not pre-sizing for large lists. If you know you'll add 100,000 items, pass the capacity upfront:
new ArrayList<>(100000). This avoids repeated array reallocations as the list grows. - Confusing
ArrayListwithLinkedList. ArrayList is fast for random access by index but slow for insertion at the front. LinkedList is the opposite. Choose based on your access pattern.
Wrapping Up
You now have a complete reference for Java's ArrayList — from creating and adding, through iterating and removing, to streams and parallel processing. The methods you'll actually use day to day are add, get, remove, size, contains, and forEach. The rest are there when you need them.
From here, you can explore:
- LinkedList — the alternative list implementation, better for frequent insertions at the front.
- HashMap and HashSet — the other two collections you'll use constantly.
- The Collections utility class — for sorting, reversing, shuffling, and binary search.
- Stream API deeper dive —
map,reduce,groupingBy, and other powerful operations. - Immutability with
List.of()— the modern way to create small fixed lists (Java 9+).
No comments:
Post a Comment