10 Hidden Pitfalls of Java List Implementations and How to Avoid Them

This article examines common traps when using Java List structures such as Arrays.asList, ArrayList, LinkedList, and CopyOnWriteArrayList, explains why they occur, and provides practical solutions—including proper conversion, avoiding unsupported operations, handling subList references, and mitigating memory and concurrency issues.

Java High-Performance Architecture
Java High-Performance Architecture
Java High-Performance Architecture
10 Hidden Pitfalls of Java List Implementations and How to Avoid Them

1. Arrays.asList conversion of primitive arrays pitfall

When converting a primitive array with Arrays.asList, the result is a list containing a single element because the primitive array is treated as one object.

int[] arr = {1, 2, 3};
List list = Arrays.asList(arr);
System.out.println(list.size()); // 1

The method signature public static <span>T</span> asList(T... a) expects an array of reference types, so a primitive array is wrapped as int[] and the list becomes List<int[]>.

Solutions:

Java 8+:

List<Integer> list = Arrays.stream(arr).boxed().collect(Collectors.toList());

Use wrapper type array directly:

Integer[] integerArr = {1,2,3}; List<Integer> list = Arrays.asList(integerArr);

2. Arrays.asList returns an unmodifiable list

The list produced by Arrays.asList does not support add or remove operations because it is an internal static class extending AbstractList without implementing those methods.

private static class ArrayList<E> extends AbstractList<E> implements RandomAccess, Serializable {
    private final E[] a;
    ArrayList(E[] array) { a = Objects.requireNonNull(array); }
    public E get(int index) { return a[index]; }
    public E set(int index, E element) { E old = a[index]; a[index] = element; return old; }
    // add/remove throw UnsupportedOperationException
}

Wrap the result in a new java.util.ArrayList to obtain a mutable list:

List<String> mutable = new ArrayList<>(Arrays.asList(arr));

3. Modifying the original array affects the list

The list returned by Arrays.asList holds a reference to the original array, so any change to the array is reflected in the list.

int[] arr = {1,2,3};
List<int[]> list = Arrays.asList(arr);
arr[2] = 4; // list now shows [1,2,4]

Solution: create a new ArrayList from the returned list to break the reference.

4. java.util.ArrayList may still reject add/remove when wrapped

Even after wrapping with new ArrayList<>(Arrays.asList(...)), attempts to modify the list during iteration can cause UnsupportedOperationException because the underlying list is still the fixed-size view.

5. SubList casting pitfalls

Calling list.subList(...) returns an internal ArrayList.SubList view, not a true ArrayList. Casting it to ArrayList throws ClassCastException.

List<String> names = new ArrayList<>();
ArrayList<String> sub = (ArrayList<String>) names.subList(0,1); // throws

The sub‑list shares the backing list; modifications to either affect the other and can trigger ConcurrentModificationException if the original list changes structurally.

6. SubList can cause OOM

Because a sub‑list retains a reference to the original large list, repeated sub‑list creation may prevent garbage collection, leading to OutOfMemoryError.

IntStream.range(0,1000).forEach(i -> {
    List<Integer> big = IntStream.range(0,100000).boxed().collect(Collectors.toList());
    data.add(big.subList(0,1)); // retains reference to big
});

Fixes:

Wrap the sub‑list in a new ArrayList to detach it.

Use stream().skip(...).limit(...).collect(Collectors.toList()) for slicing.

7. LinkedList insertion is not always faster than ArrayList

Benchmarking random insertions shows that LinkedList can be significantly slower than ArrayList because each insertion requires node traversal.

StopWatch sw = new StopWatch();
int count = 100000;
// ArrayList insert benchmark
List<Integer> array = IntStream.rangeClosed(1,count).boxed().collect(Collectors.toCollection(ArrayList::new));
IntStream.rangeClosed(0,count).forEach(i -> array.add(ThreadLocalRandom.current().nextInt(count),1));
// LinkedList insert benchmark
List<Integer> linked = IntStream.rangeClosed(1,count).boxed().collect(Collectors.toCollection(LinkedList::new));
IntStream.rangeClosed(0,count).forEach(i -> linked.add(ThreadLocalRandom.current().nextInt(count),1));

Results indicate LinkedList insertions can be 2‑3× slower for large random positions.

8. CopyOnWriteArrayList memory overhead

Each write creates a fresh copy of the internal array, doubling memory usage for large lists and potentially triggering full GC pauses.

public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] elements = getArray();
        int len = elements.length;
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        newElements[len] = e;
        setArray(newElements);
        return true;
    } finally { lock.unlock(); }
}

9. CopyOnWriteArrayList iterator is weakly consistent

The iterator works on a snapshot of the array taken at iterator creation; subsequent modifications are invisible to the iterator.

Iterator<String> it = list.iterator();
// another thread modifies list
while (it.hasNext()) {
    System.out.println(it.next()); // sees only original elements
}

10. CopyOnWriteArrayList iterator does not support modification

Calling remove, add, or set on the iterator always throws UnsupportedOperationException because the iterator is read‑only.

Iterator<String> it = list.iterator();
while (it.hasNext()) {
    if ("test1".equals(it.next())) {
        it.remove(); // throws UnsupportedOperationException
    }
}

Summary

Understanding the characteristics and limitations of Java collection utilities—especially Arrays.asList, ArrayList, LinkedList, and CopyOnWriteArrayList —helps avoid subtle bugs such as unexpected list sizes, unsupported operations, memory leaks, and concurrency issues. Choose the appropriate container and apply the recommended work‑arounds to achieve reliable and performant code.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaArrays.asListListArrayListCopyOnWriteArrayListLinkedList
Java High-Performance Architecture
Written by

Java High-Performance Architecture

Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.