ArrayList in Java: Complete Guide with Examples
ArrayList is the most widely used collection class in Java. It's a resizable array that grows automatically as you add elements, with fast random access and a rich API. This guide covers every operation you need in practice.
Creating an ArrayList
import java.util.ArrayList;
import java.util.List;
// Empty ArrayList
List<String> names = new ArrayList<>();
// With initial capacity (optional optimization)
List<String> names2 = new ArrayList<>(100);
// From another collection
List<String> copy = new ArrayList<>(names);
// From fixed values (Java 9+)
List<String> fromValues = new ArrayList<>(List.of("Alice", "Bob", "Carol"));
Always declare the variable as List<T> rather than ArrayList<T>. It makes it easy to swap the implementation later without touching the callers.
Adding elements
List<String> names = new ArrayList<>();
names.add("Alice"); // append to the end
names.add(0, "Zero"); // insert at a specific index
names.addAll(List.of("Bob", "Carol")); // append multiple
names.addAll(1, List.of("A", "B")); // insert multiple at index
Accessing elements
String first = names.get(0); // by index β fast, O(1)
int pos = names.indexOf("Bob"); // find index β O(n), -1 if absent
boolean has = names.contains("Alice"); // O(n)
int size = names.size(); // number of elements
Modifying and removing
names.set(0, "ALICE"); // replace element at index
names.remove(0); // remove by index β shifts elements left
names.remove("Bob"); // remove first occurrence by value
names.removeIf(n -> n.startsWith("A")); // conditional removal (Java 8+)
names.clear(); // empty the list
Pitfall: list.remove(1) calls remove(int index). To remove an Integer by value, box explicitly: list.remove(Integer.valueOf(1)).
Iterating
// Preferred: for-each loop
for (String n : names) {
System.out.println(n);
}
// With index
for (int i = 0; i < names.size(); i++) {
System.out.println(i + ": " + names.get(i));
}
// Stream API (Java 8+)
names.stream()
.filter(n -> n.length() > 3)
.forEach(System.out::println);
// Iterator β needed when removing during iteration
Iterator<String> it = names.iterator();
while (it.hasNext()) {
if (it.next().isEmpty()) it.remove();
}
Never modify a list with list.remove() inside a for-each loop β you'll get ConcurrentModificationException. Use the iterator's remove() or removeIf().
Converting to an array
String[] array = names.toArray(new String[0]);
// Modern form (Java 11+)
String[] array2 = names.toArray(String[]::new);
Sorting
import java.util.Collections;
import java.util.Comparator;
Collections.sort(names); // natural order
Collections.sort(names, Comparator.reverseOrder()); // reverse
names.sort(Comparator.comparing(String::length)); // custom comparator
Capacity vs size
An ArrayList has two related numbers: size (number of elements) and capacity (length of the internal array). When capacity is full, it grows by ~50%, which takes O(n) occasionally but averages to O(1) per append.
If you know the final size, pass it to the constructor: new ArrayList<>(1_000_000). Avoids repeated reallocations.
ArrayList vs LinkedList
| Operation | ArrayList | LinkedList |
|---|---|---|
| get(i) | O(1) | O(n) |
| add(e) at the end | amortized O(1) | O(1) |
| add(i, e) in the middle | O(n) | O(n) to reach, then O(1) |
| remove(i) | O(n) | O(n) to reach, then O(1) |
| Memory per element | ~4 bytes overhead | ~40 bytes overhead |
In practice, ArrayList wins almost every real-world workload. Reach for LinkedList only when you truly need a queue or deque, and even then ArrayDeque is usually faster.
Thread safety
ArrayList is not thread-safe. For concurrent access, use one of:
Collections.synchronizedList(new ArrayList<>())β synchronized wrapper; still needs external sync for iterationCopyOnWriteArrayListβ safe, but each write copies the array; good for read-heavy workloadsConcurrentLinkedQueueorCollections.unmodifiableList()for immutable sharing
Immutable lists
Since Java 9, List.of(...) creates a compact immutable list. Since Java 10, List.copyOf(anyList) copies into an immutable one.
List<String> immutable = List.of("a", "b", "c");
immutable.add("d"); // β UnsupportedOperationException
Common gotchas
- Autoboxing overhead:
List<Integer>stores boxed integers. In hot loops, preferint[]or primitive-specialized libraries (Eclipse Collections). - subList is a view: modifying the original list invalidates the sublist. Wrap in
new ArrayList<>(list.subList(...))if you need independence. - toArray without type:
list.toArray()returnsObject[], notString[].
For 95% of use cases, ArrayList is the right default. Master these operations and you've mastered the Java Collections Framework's most useful class.