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

OperationArrayListLinkedList
get(i)O(1)O(n)
add(e) at the endamortized O(1)O(1)
add(i, e) in the middleO(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 iteration
  • CopyOnWriteArrayList β€” safe, but each write copies the array; good for read-heavy workloads
  • ConcurrentLinkedQueue or Collections.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, prefer int[] 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() returns Object[], not String[].

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.