Java ArrayList

INTERMEDIATE ~8 min read Tutorial

ArrayList is the most-used implementation of the List interface. It stores elements in a backing array that grows automatically when full. You get O(1) random access by index, O(1) amortised append, and O(n) insertion or removal in the middle.

This tutorial covers creation, add/get/set/remove, iteration, generics, the enhanced for-each loop, sorting, conversion to and from arrays, and the immutable factory methods that often replace ArrayList in modern Java.

1. Creating an ArrayList

java
import java.util.ArrayList;
import java.util.List;

class=class="tok-str">"tok-cmt">// empty - will grow as we add
List<String> names = new ArrayList<>();

class=class="tok-str">"tok-cmt">// with an initial capacity (avoids resizes)
List<String> buffer = new ArrayList<>(class="tok-num">1000);

class=class="tok-str">"tok-cmt">// from existing collection
List<String> copy = new ArrayList<>(names);

class=class="tok-str">"tok-cmt">// from Java class="tok-num">9 immutable factory
List<String> colors = List.of("red", "green", "blue");   class=class="tok-str">"tok-cmt">// immutable

class=class="tok-str">"tok-cmt">// from an array
String[] arr = {"a", "b"};
List<String> list = new ArrayList<>(java.util.Arrays.asList(arr));
Always use the generic type parameter

Without <String> you get a raw ArrayList that can hold any object, leading to ClassCastException at runtime and losing all compile-time safety. Modern Java always uses generics.

2. Adding, Getting, Setting

java
List<String> list = new ArrayList<>();
list.add("Alice");
list.add("Bob");
list.add(class="tok-num">0, "First");   class=class="tok-str">"tok-cmt">// insert at index class="tok-num">0 - shifts others

class=class="tok-str">"tok-cmt">// [First, Alice, Bob]
System.out.println(list);

class=class="tok-str">"tok-cmt">// access
String first = list.get(class="tok-num">0);          class=class="tok-str">"tok-cmt">// "First"
int size = list.size();              class=class="tok-str">"tok-cmt">// class="tok-num">3

class=class="tok-str">"tok-cmt">// replace
list.set(class="tok-num">1, "Alicia");               class=class="tok-str">"tok-cmt">// replaces "Alice"

class=class="tok-str">"tok-cmt">// bulk
list.addAll(List.of("Carol", "Dave"));
class=class="tok-str">"tok-cmt">// [First, Alicia, Bob, Carol, Dave]

The backing array grows automatically: when full, ArrayList allocates a new array (typically 50% larger) and copies. This makes add O(1) amortised — fast enough for almost all use cases.

3. Removing Elements

java
List<String> list = new ArrayList<>(List.of("a","b","c","d","e"));

class=class="tok-str">"tok-cmt">// remove by index
String removed = list.remove(class="tok-num">2);    class=class="tok-str">"tok-cmt">// "c", list is now [a, b, d, e]

class=class="tok-str">"tok-cmt">// remove by value (first match)
boolean wasRemoved = list.remove("d");   class=class="tok-str">"tok-cmt">// true, list is [a, b, e]

class=class="tok-str">"tok-cmt">// remove all matching a predicate (Java class="tok-num">8+)
list.removeIf(s -> s.startsWith("a"));   class=class="tok-str">"tok-cmt">// removes "a"

class=class="tok-str">"tok-cmt">// removeAll / retainAll - bulk operations
list.removeAll(List.of("b"));
list.retainAll(List.of("e"));   class=class="tok-str">"tok-cmt">// keep only "e"

class=class="tok-str">"tok-cmt">// clear all
list.clear();

remove(int) shifts all elements after the removed index left by one — O(n). remove(Object) searches and removes the first equal element. If your list is of Integer, be careful: list.remove(2) removes by index, not by value — use list.remove(Integer.valueOf(2)) to remove the value.

4. Iterating

java
List<String> list = List.of("a", "b", "c");

class=class="tok-str">"tok-cmt">// class="tok-num">1. enhanced for-each
for (String s : list) {
    System.out.println(s);
}

class=class="tok-str">"tok-cmt">// class="tok-num">2. indexed for
for (int i = class="tok-num">0; i < list.size(); i++) {
    System.out.println(i + ": " + list.get(i));
}

class=class="tok-str">"tok-cmt">// class="tok-num">3. iterator with remove
var it = list.iterator();
while (it.hasNext()) {
    String s = it.next();
    if (s.isBlank()) it.remove();   class=class="tok-str">"tok-cmt">// safe removal during iteration
}

class=class="tok-str">"tok-cmt">// class="tok-num">4. forEach with method reference
list.forEach(System.out::println);
Modifying while iterating

Calling list.remove(x) inside a for-each loop throws ConcurrentModificationException. Use the iterator's own remove() method, or filter into a new list.

5. Size, Empty, Contains

java
List<String> list = new ArrayList<>(List.of("a","b","c"));

int n = list.size();              class=class="tok-str">"tok-cmt">// class="tok-num">3
boolean empty = list.isEmpty();   class=class="tok-str">"tok-cmt">// false
boolean hasA = list.contains("a");   class=class="tok-str">"tok-cmt">// true
int idx = list.indexOf("b");      class=class="tok-str">"tok-cmt">// class="tok-num">1
int last = list.lastIndexOf("c");   class=class="tok-str">"tok-cmt">// class="tok-num">2

class=class="tok-str">"tok-cmt">// sub-list (view, not copy)
List<String> sub = list.subList(class="tok-num">0, class="tok-num">2);   class=class="tok-str">"tok-cmt">// [a, b]

6. Sorting

Use List.sort with a Comparator, or Collections.sort (the older form):

java
import java.util.Comparator;
import java.util.List;

List<Integer> nums = new ArrayList<>(List.of(class="tok-num">5, class="tok-num">3, class="tok-num">8, class="tok-num">1, class="tok-num">9, class="tok-num">2));

class=class="tok-str">"tok-cmt">// natural order
nums.sort(Comparator.naturalOrder());
class=class="tok-str">"tok-cmt">// [class="tok-num">1, class="tok-num">2, class="tok-num">3, class="tok-num">5, class="tok-num">8, class="tok-num">9]

class=class="tok-str">"tok-cmt">// reverse
nums.sort(Comparator.reverseOrder());
class=class="tok-str">"tok-cmt">// [class="tok-num">9, class="tok-num">8, class="tok-num">5, class="tok-num">3, class="tok-num">2, class="tok-num">1]

class=class="tok-str">"tok-cmt">// custom comparator - sort strings by length
List<String> words = new ArrayList<>(List.of("banana","fig","apple","date"));
words.sort(Comparator.comparingInt(String::length));
class=class="tok-str">"tok-cmt">// [fig, date, apple, banana]

class=class="tok-str">"tok-cmt">// chain comparators
words.sort(Comparator.comparingInt(String::length)
                     .thenComparing(Comparator.naturalOrder()));

For reverse order, use Comparator.reverseOrder() or Comparator.naturalOrder().reversed().

7. Functional Operations

ArrayList implements Iterable, so it works seamlessly with the Streams API:

java
List<Integer> nums = List.of(class="tok-num">1, class="tok-num">2, class="tok-num">3, class="tok-num">4, class="tok-num">5, class="tok-num">6);

class=class="tok-str">"tok-cmt">// filter
List<Integer> evens = nums.stream()
    .filter(n -> n % class="tok-num">2 == class="tok-num">0)
    .toList();   class=class="tok-str">"tok-cmt">// Java class="tok-num">16+ toList() returns immutable list
class=class="tok-str">"tok-cmt">// [class="tok-num">2, class="tok-num">4, class="tok-num">6]

class=class="tok-str">"tok-cmt">// map
List<String> names = List.of("alice","bob","carol");
List<String> upper = names.stream()
    .map(String::toUpperCase)
    .toList();
class=class="tok-str">"tok-cmt">// [ALICE, BOB, CAROL]

class=class="tok-str">"tok-cmt">// reduce
int sum = nums.stream().mapToInt(Integer::intValue).sum();   class=class="tok-str">"tok-cmt">// class="tok-num">21

class=class="tok-str">"tok-cmt">// collect into an ArrayList
List<Integer> gt3 = nums.stream()
    .filter(n -> n > class="tok-num">3)
    .collect(java.util.stream.Collectors.toCollection(ArrayList::new));

8. Immutable Lists (Java 9+)

For fixed-size, immutable lists, prefer List.of. It produces a list that cannot be modified, is more memory-efficient, and is thread-safe:

java
class=class="tok-str">"tok-cmt">// immutable, fixed-size, null-hostile
List<String> weekdays = List.of(
    "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"
);

class=class="tok-str">"tok-cmt">// these all throw UnsupportedOperationException
class=class="tok-str">"tok-cmt">// weekdays.add("Extra");
class=class="tok-str">"tok-cmt">// weekdays.remove(class="tok-num">0);
class=class="tok-str">"tok-cmt">// weekdays.set(class="tok-num">0, "MON");

class=class="tok-str">"tok-cmt">// copy a mutable list into an immutable one
List<String> mutable = new ArrayList<>(List.of("a","b","c"));
List<String> frozen = List.copyOf(mutable);   class=class="tok-str">"tok-cmt">// Java class="tok-num">10+
class=class="tok-str">"tok-cmt">// frozen.add("d");   // throws

class=class="tok-str">"tok-cmt">// singleton and empty lists
List<String> one = List.of("only");
List<String> none = List.of();

For converting a mutable ArrayList to immutable, use List.copyOf(list) (Java 10+).

9. Performance Cheat Sheet

OperationTime complexityNotes
get(i)O(1)Direct array access
set(i, x)O(1)Direct array access
add(x) (append)O(1) amortisedOccasional resize O(n)
add(i, x) (insert)O(n)Shifts elements right
remove(i)O(n)Shifts elements left
contains(x)O(n)Linear scan; use HashSet for O(1)
Memory~8 bytes per element + array overheadBacking array plus capacity slack

Exercises

  1. Create an ArrayList of 10 random integers, then print them sorted ascending.
  2. Add 1000 strings "item N" to an ArrayList, then remove all that end with an even digit.
  3. Convert an ArrayList of Strings to a String[] using toArray.
  4. Use List.of to create an immutable list of weekdays and confirm that add throws UnsupportedOperationException.
  5. Use a Stream to find the average of an ArrayList of doubles.