Java ArrayList
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
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));
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
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
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
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);
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
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):
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:
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:
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
| Operation | Time complexity | Notes |
|---|---|---|
get(i) | O(1) | Direct array access |
set(i, x) | O(1) | Direct array access |
add(x) (append) | O(1) amortised | Occasional 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 overhead | Backing array plus capacity slack |
Exercises
- Create an ArrayList of 10 random integers, then print them sorted ascending.
- Add 1000 strings
"item N"to an ArrayList, then remove all that end with an even digit. - Convert an ArrayList of Strings to a
String[]usingtoArray. - Use
List.ofto create an immutable list of weekdays and confirm thataddthrowsUnsupportedOperationException. - Use a Stream to find the average of an ArrayList of doubles.