🗃️ Collections Framework · Intermediate

Arrays.asList gotchas in Java

Fixed-size, backed by the array, add() throws UnsupportedOperationException.

🧩 The mysteryArrays.asList("a", "b") gives you something that says it's a List and even prints like one. Then you call add() and it throws. What did you actually get?

A list-shaped window on an array

**Arrays.asList wraps an array in a List view. An array can't grow, so the list is fixed-size**: set() works, but **add() and remove() throw UnsupportedOperationException**. It's not a java.util.ArrayList either, but a private class with the same simple name.

List<String> l = Arrays.asList("a", "b");
l.set(0, "z");  // ok: [z, b]
l.add("c");     // UnsupportedOperationException
🔮 Predict it

Your turn

What happens?

List<String> l = Arrays.asList("p", "q");
l.remove(0);
System.out.println(l);
  1. Prints [q]
  2. Prints [p, q]
  3. Throws UnsupportedOperationException
Show the answer

Removing would shrink the backing array, which is impossible. **remove throws UnsupportedOperationException** at runtime, just like add.

Writes go both ways

The list and the array share the same storage. list.set changes the array, and writing to the array shows up in the list.

String[] arr = {"a", "b"};
List<String> list = Arrays.asList(arr);
list.set(0, "z");   // arr[0] is now "z"
arr[1] = "q";       // list.get(1) is "q"
🔮 Predict it

Both directions

What does this print?

String[] arr = {"a", "b"};
var list = Arrays.asList(arr);
arr[0] = "X";
list.set(1, "Y");
String a = Arrays.toString(arr);
System.out.println(list + " " + a);
  1. [X, Y] [X, Y]
  2. [a, Y] [X, b]
  3. [X, b] [a, Y]
Show the answer

One shared storage: the array write and the list write both land in the same place, so both print [X, Y].

⚠️ The trap

The primitive array trap

Generics can't hold primitives, so T can't be int. With an int[], **T becomes int[] and you get a List<int[]> with ONE element**: the array itself. Use Integer[], or Arrays.stream(nums).boxed().toList().

int[] nums = {1, 2, 3};
Arrays.asList(nums).size(); // 1, not 3!

Need a list you can grow?

✗ Fixed or broken
var l = Arrays.asList("a", "b");
l.add("c"); // throws
var a = (ArrayList<String>) l; // CCE

Fixed-size, and the cast fails with ClassCastException: it isn't a java.util.ArrayList.

✓ Growable copy
var l = new ArrayList<>(
    Arrays.asList("a", "b"));
l.add("c"); // [a, b, c]

Copy into a real ArrayList. (List.of won't help: it's immutable.)

💼 In the real world

Where it bites

Arrays.asList is great for quick fixed lists in tests and for passing an array to an API that wants a List. Bugs appear when that list travels to code that tries to add to it, and the exception fires far from where the list was made.

Key takeaways

  1. Fixed size: add/remove throw UnsupportedOperationException
  2. set() writes through to the array, and array writes show in the list
  3. Arrays.asList(intArray) gives a List<int[]> of size 1
  4. Need a growable copy? new ArrayList<>(Arrays.asList(...))

💡 Arrays.asList puts a List-shaped frame around an existing array — you can repaint the pictures, but you can't add more slots to the frame.

🤯 Did you know?

Print Arrays.asList("a").getClass() and you'll see class java.util.Arrays$ArrayList: a private nested class that shares its simple name with the real ArrayList, which confuses plenty of developers.

Practice questions

What happens when this runs?

List<String> list = Arrays.asList("a", "b");
list.add("c");
System.out.println(list);
  1. [a, b, c]
  2. Throws UnsupportedOperationException
  3. [a, b]
  4. Compile error
Check your answer

Throws UnsupportedOperationException. The list is fixed-size because it's backed by an array, which can't grow. add() throws at runtime.

What does this print?

String[] arr = {"a", "b", "c"};
List<String> list = Arrays.asList(arr);
list.set(0, "z");
arr[2] = "q";
System.out.println(Arrays.toString(arr) + list);
  1. [z, b, q][z, b, q]
  2. [a, b, q][z, b, c]
  3. [z, b, c][z, b, q]
  4. [a, b, c][z, b, c]
Check your answer

[z, b, q][z, b, q]. The list and the array share the same storage. list.set changes the array and the array assignment shows up in the list.

Next: for 25 years, getting the last element of a list meant list.get(list.size() - 1). Java 21 finally fixed that.