Arrays.asList() vs new ArrayList() in Java: Key Differences

Arrays.asList() vs new ArrayList() in Java explained with examples, covering fixed size, the shared array, List.of(), primitive arrays and conversions.

Arrays.asList view pointing to the original array, and a new ArrayList holding its own copied array that references the same element objects

The method Arrays.asList(array) returns a fixed-size list backed by the array, whereas new ArrayList<>(Arrays.asList(array)) copies the elements into a new, resizable ArrayList. With the first one, the list and the array share the same storage, so a change through one is visible through the other, and add() or remove() throws UnsupportedOperationException. That shared storage is the main point in any Arrays.asList() vs new ArrayList() comparison.

We use Arrays.asList() to pass an existing array to a method that expects a List, and the ArrayList copy when the code must add or remove elements later.

The following example creates both lists from the same array and changes them.

String[] colors = {"red", "green", "blue"};
List<String> view = Arrays.asList(colors);                     // [red, green, blue]
List<String> copy = new ArrayList<>(Arrays.asList(colors));    // [red, green, blue]
view.set(0, "pink");                                           // colors = [pink, green, blue]
boolean added = copy.add("black");                             // true
boolean failed = view.add("black");                            // UnsupportedOperationException

Notice that set() on the view changed the array itself. We compare the returned types, adding and removing, the shared array, and the newer List.of(), and we end with the conversions and traps that cause real bugs.

1. What Arrays.asList() and new ArrayList() Return

The class Arrays in java.util holds static helper methods for arrays. Its method Arrays.asList() has the signature public static <T> List<T> asList(T… a), so it accepts an array or separate values, and it returns a List view of that array. The view copies nothing and wraps the array, so the list has the same fixed size as the array.

The ArrayList constructor ArrayList(Collection) takes any collection and copies its elements into a new internal array. The result is an independent, resizable list.

String[] sizes = {"S", "M", "L"};
List<String> fixed = Arrays.asList(sizes);                    // [S, M, L]
ArrayList<String> growable = new ArrayList<>(Arrays.asList(sizes));    // [S, M, L]

2. Difference between Arrays.asList(array) and new ArrayList(Arrays.asList(array))

Both expressions print the same elements, so the difference appears only when we change the list or the array.

2.1. The Returned List

The list from Arrays.asList() is not a java.util.ArrayList. It is an instance of the private nested class java.util.Arrays$ArrayList, which has the same simple name but supports fewer operations.

String[] sizes = {"S", "M", "L"};
String viewType = Arrays.asList(sizes).getClass().getName();                    // "java.util.Arrays$ArrayList"
String copyType = new ArrayList<>(Arrays.asList(sizes)).getClass().getName();   // "java.util.ArrayList"

Because the two classes differ, a cast from the view to ArrayList compiles but fails at runtime. We declare the variable as List, or we create a real copy with the constructor.

List<String> view = Arrays.asList("S", "M");
ArrayList<String> cast = (ArrayList<String>) view;             // ClassCastException

2.2. Adding and Removing Elements

The list from Arrays.asList() has a fixed size, because an array cannot grow or shrink. Every method that changes the size, such as add(), remove(), clear() or removeIf() with a match, throws UnsupportedOperationException. Methods that only replace or reorder elements work, such as set(), sort() and replaceAll().

String[] days = {"tue", "mon", "wed"};
List<String> view = Arrays.asList(days);
view.sort(null);                                   // view = [mon, tue, wed]
view.replaceAll(String::toUpperCase);              // view = [MON, TUE, WED]
boolean removed = view.remove("MON");              // UnsupportedOperationException

The ArrayList copy supports every List operation, so it can grow, shrink and change.

String[] days = {"mon", "tue"};
List<String> copy = new ArrayList<>(Arrays.asList(days));
copy.add("wed");                                   // copy = [mon, tue, wed]
copy.remove("mon");                                // copy = [tue, wed]

A typical bug starts when a web app reads allowed roles from a comma-separated setting with Arrays.asList(value.split(“,”)) and passes the list on. Months later, another developer adds a default role with roles.add(“guest”), and the endpoint starts failing with UnsupportedOperationException in production. Wrapping the result in new ArrayList<>(…) at the place where the list is created prevents the bug.

2.3. Internal Reference for Underlying Array and List

The list from Arrays.asList() keeps a reference to the original array and reads and writes its slots. No second array exists, which is why creating the view takes constant time no matter how large the array is.

Arrays.asList view pointing to the original array, and a new ArrayList holding its own copied array that references the same element objects
The method Arrays.asList() wraps the same array, while new ArrayList() copies the references into an array of its own

The constructor new ArrayList<>(collection) calls toArray() on the collection and keeps the copied array as its own storage. Copying takes time proportional to the number of elements, and after it the list and the original array are separate objects. The elements themselves are not copied, so both arrays hold references to the same element objects.

2.4. Changes in Underlying Array on Changing the List Items

Since the view and the array share storage, a change through either one is visible in the other. With the ArrayList copy, a change to one has no effect on the other.

String[] fruits = {"apple", "banana"};
List<String> view = Arrays.asList(fruits);
List<String> copy = new ArrayList<>(Arrays.asList(fruits));
fruits[1] = "cherry";                              // view = [apple, cherry]
copy.set(0, "mango");                              // fruits = [apple, cherry]
String fromCopy = copy.toString();                 // "[mango, banana]"

The copy is shallow, though. When the elements are mutable objects, both lists point to the same objects, so changing an object is visible everywhere. For an independent copy of the elements, see deep copying an ArrayList.

StringBuilder[] notes = {new StringBuilder("buy")};
List<StringBuilder> copy = new ArrayList<>(Arrays.asList(notes));
notes[0].append(" milk");
String shared = copy.get(0).toString();            // "buy milk"

3. Arrays.asList() vs List.of()

Java 9 added List.of(), which looks similar but returns an unmodifiable list. The three options differ in more than size, so the full comparison helps when we pick one.

BehaviorArrays.asList(array)new ArrayList<>(…)List.of(array)
add(), remove()UnsupportedOperationExceptionAllowedUnsupportedOperationException
set(), sort()Allowed, writes to the arrayAllowedUnsupportedOperationException
Later changes to the arrayVisible in the listNot visibleNot visible
null elementsAllowedAllowedNullPointerException
contains(null)Returns falseReturns falseNullPointerException
Copies the elementsNoYesYes
String[] tags = {"java", "lists"};
List<String> immutable = List.of(tags);
tags[0] = "kotlin";                                // immutable = [java, lists]
List<String> withNull = Arrays.asList("a", null);  // [a, null]
List<String> noNull = List.of("a", null);          // NullPointerException

For fixed values in modern code, we write List.of(“a”, “b”) when the list never changes and new ArrayList<>(List.of(“a”, “b”)) when it must change. More options are in initializing a List in one line.

4. Arrays of Primitives

The method Arrays.asList() takes a generic array T…, and generics cannot hold primitives. When we pass an int[], the whole array becomes one element, and the result is a List<int[]> with size 1.

int[] scores = {90, 75, 60};
List<int[]> wrong = Arrays.asList(scores);
int size = wrong.size();                                          // 1
List<Integer> boxed = Arrays.stream(scores).boxed().toList();     // [90, 75, 60]
List<Integer> editable = new ArrayList<>(boxed);                  // [90, 75, 60]

For arrays of primitives, we box the values with Arrays.stream(array).boxed(), or we start with an Integer[] instead of an int[].

5. When to Use Arrays.asList() vs new ArrayList()

The choice depends on whether the list has to grow, shrink or stay in sync with the array.

  • Use Arrays.asList(array) to pass an array to an API that reads a List, or when list and array must stay in sync. It costs nothing, because no data is copied.
  • Use new ArrayList<>(Arrays.asList(array)) when the list must grow or shrink, or when it must not change if someone edits the array later. The copy takes time and memory proportional to the size of the array.
  • Use List.of(…) for fixed values that nobody may change, including through set().

The old advice to use Arrays.asList() for an unmodifiable list is wrong. Its set() and sort() methods still change the list and the array, so code that must stay read-only needs List.of(), List.copyOf() or Collections.unmodifiableList().

6. Converting Arrays.asList() to ArrayList

An independent ArrayList is what most calling code expects, and the JDK offers several ways to build one from an array or from an existing view.

6.1. Using ArrayList Constructor

The constructor is the shortest way and copies all elements in one step. It also converts a list that came from Arrays.asList() in other code, for example a method parameter.

String[] cities = {"Paris", "Rome"};
ArrayList<String> list = new ArrayList<>(Arrays.asList(cities));    // [Paris, Rome]
list.add("Oslo");                                                    // list = [Paris, Rome, Oslo]

6.2. Using Collections.addAll() or a Stream

The method Collections.addAll() adds the array elements to a list we already have, without creating the intermediate view. A stream helps when we also filter or map the elements during the conversion, and a plain for-each loop with add() still works for both.

String[] cities = {"Paris", "Rome", "Oslo"};
List<String> target = new ArrayList<>(List.of("Lima"));
Collections.addAll(target, cities);                      // target = [Lima, Paris, Rome, Oslo]
ArrayList<String> shortNames = Arrays.stream(cities)
        .filter(c -> c.length() == 4)
        .collect(Collectors.toCollection(ArrayList::new));
String result = shortNames.toString();                   // "[Rome, Oslo]"

7. Arrays.asList() FAQs

Developers search for these questions after a list behaves differently than expected.

7.1. Why does Arrays.asList() throw UnsupportedOperationException on add()?

The returned list is a fixed-size view of an array, and an array cannot grow. Its class extends AbstractList without overriding add() and remove(), so those methods throw UnsupportedOperationException. Wrapping the view in new ArrayList<>(…) fixes it.

7.2. Is Arrays.asList() modifiable?

Partly. We can replace and reorder elements with set(), sort() and replaceAll(), and those changes write through to the array. We cannot change the size.

7.3. Does Arrays.asList() copy the array?

No. The list wraps the original array, so creating it takes constant time, and changes in either one are visible in the other. The ArrayList constructor and List.of(array) copy the elements.

7.4. Can we cast Arrays.asList() to ArrayList?

No. The cast compiles, but it throws ClassCastException at runtime, because the actual class is java.util.Arrays$ArrayList. We use new ArrayList<>(Arrays.asList(array)) instead.

7.5. What is the difference between Arrays.asList() and List.of()?

The list from List.of() is unmodifiable, copies the array and rejects null elements. The list from Arrays.asList() allows set(), writes through to the array and accepts null, as the table in section 3 shows.

8. Conclusion

The method Arrays.asList() gives a fixed-size list view over an array. It is cheap and stays in sync with the array, but add() and remove() throw UnsupportedOperationException, and it is not a java.util.ArrayList.

The constructor call new ArrayList<>(Arrays.asList(array)) copies the element references into an independent, resizable list. For values that must never change, List.of() is the better choice, and for primitive arrays we box the values with a stream first.

9. References

Happy Learning !!

Source Code on Github

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.