Basics

7. Arrays vs ArrayList

T[] is a fixed-size, covariant array; ArrayList<T> is a resizable, type-safe list. Arrays.asList() returns a fixed-size list backed by the original array โ€” a common trap.

Java 21 (core language)

Explanation

A plain Java array (int[], String[]) has a fixed length set at creation time. Arrays are covariant (String[] is an Object[]), which can cause ArrayStoreException at runtime. They have no built-in methods beyond length; use java.util.Arrays for utilities.

ArrayList<T> dynamically resizes (doubles capacity when full), is not covariant (generics are invariant), and provides the full List API. Default initial capacity is 10; use new ArrayList<>(expectedSize) when you know the size to avoid unnecessary copies.

The notorious trap: Arrays.asList(arr) returns a fixed-size List backed by the array. You can call set() (it mutates the underlying array) but calling add() or remove() throws UnsupportedOperationException. For a true independent mutable list, use new ArrayList<>(Arrays.asList(arr)) or new ArrayList<>(List.of(...)).

Example

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

public class ArraysVsArrayList {
    public static void main(String[] args) {
        // Fixed-size array
        String[] arr = {"a", "b", "c"};
        arr[0] = "x"; // OK
        // arr has no add/remove

        // Arrays.asList trap: fixed-size list backed by array
        List<String> fixed = Arrays.asList("a", "b", "c");
        fixed.set(0, "x");      // OK โ€” mutates backing array
        try {
            fixed.add("d");     // UnsupportedOperationException!
        } catch (UnsupportedOperationException e) {
            System.out.println("Cannot add to Arrays.asList list");
        }

        // True mutable copy
        List<String> mutable = new ArrayList<>(Arrays.asList("a", "b", "c"));
        mutable.add("d"); // OK
        System.out.println(mutable); // [a, b, c, d]

        // List.of: truly immutable (Java 9+), no null elements
        List<String> immutable = List.of("a", "b", "c");
        try {
            immutable.add("d"); // UnsupportedOperationException
        } catch (UnsupportedOperationException e) {
            System.out.println("List.of is immutable");
        }

        // ArrayList with pre-sized capacity
        List<Integer> presized = new ArrayList<>(100);
        for (int i = 0; i < 100; i++) presized.add(i);
        System.out.println("Size: " + presized.size());
    }
}

Sources