Fundamentals 19 min read

Why Your Java List Operations May Fail: Hidden Pitfalls and Fixes

This article reveals common pitfalls when using Java List implementations—such as Arrays.asList with primitive arrays, unsupported add/remove operations, side‑effects on the original array, subList memory leaks, LinkedList performance myths, and CopyOnWriteArrayList’s memory and iterator quirks—while providing practical solutions and best‑practice recommendations.

Java Backend Technology
Java Backend Technology
Java Backend Technology
Why Your Java List Operations May Fail: Hidden Pitfalls and Fixes

Introduction

We discuss several hidden pitfalls of List operations in real‑world Java development and how to solve them.

Arrays.asList with Primitive Arrays

Converting a primitive int[] with Arrays.asList yields a list of size 1 because the method receives the whole array as a single object ( List<int[]>).

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

Solution 1: Use Arrays.stream(arr).boxed() (Java 8+).

List<Integer> collect = Arrays.stream(arr).boxed().collect(Collectors.toList());
System.out.println(collect.size()); // 3
System.out.println(collect.get(0).getClass()); // class java.lang.Integer

Solution 2: Declare the array with wrapper type.

Integer[] integerArr = {1, 2, 3};
List<Integer> integerList = Arrays.asList(integerArr);
System.out.println(integerList.size()); // 3

Arrays.asList Returns an Unmodifiable List

The list returned by Arrays.asList cannot add or remove elements because it is an internal ArrayList class that extends AbstractList without implementing add or remove.

private static class ArrayList<E> extends AbstractList<E> implements RandomAccess, Serializable { ... }
public boolean add(E e) { throw new UnsupportedOperationException(); }
public E remove(int index) { throw new UnsupportedOperationException(); }

Modifying the Original Array Affects the List

The internal ArrayList 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 sees the change

Solution: Wrap the result in a new ArrayList to break the reference.

List<Integer> list = new ArrayList<>(Arrays.asList(arr));

subList Returns a View, Not a New List

subList

produces a view backed by the original list. Casting it to ArrayList causes ClassCastException because the actual class is ArrayList$SubList.

ArrayList strings = (ArrayList) names.subList(0, 1); // throws ClassCastException

Modifying the sub‑list changes the original list, and modifying the original list while iterating the sub‑list throws ConcurrentModificationException. Use a new ArrayList to detach.

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

subList Can Cause OOM

Holding many sub‑list views prevents the large backing list from being garbage‑collected, leading to OutOfMemoryError.

for (int i = 0; i < 1000; i++) {
    List<Integer> collect = IntStream.range(0, 100000).boxed().collect(Collectors.toList());
    data.add(collect.subList(0, 1)); // strong reference keeps the 100k list alive
}

Solution: Create a new list from the sub‑list or use skip / limit streams.

List<Integer> list = new ArrayList<>(collect.subList(0, 1));
// or
List<Integer> list = collect.stream().skip(0).limit(1).collect(Collectors.toList());

LinkedList Insertion Is Not Always Faster

Benchmarks show that random insertions into a LinkedList are slower than into an ArrayList because each insertion requires node traversal.

StopWatch stopWatch = new StopWatch();
int elementCount = 100000;
// ArrayList insert benchmark
List<Integer> arrayList = IntStream.rangeClosed(1, elementCount).boxed().collect(Collectors.toCollection(ArrayList::new));
IntStream.rangeClosed(0, elementCount).forEach(i -> arrayList.add(ThreadLocalRandom.current().nextInt(elementCount), 1));
// LinkedList insert benchmark
List<Integer> linkedList = IntStream.rangeClosed(1, elementCount).boxed().collect(Collectors.toCollection(LinkedList::new));
IntStream.rangeClosed(0, elementCount).forEach(i -> linkedList.add(ThreadLocalRandom.current().nextInt(elementCount), 1));
System.out.println(stopWatch.prettyPrint());

Conclusion: Use LinkedList only for head/tail operations after profiling.

CopyOnWriteArrayList Memory Overhead

Each write creates a new copy of the internal array, doubling memory usage and increasing GC pressure, especially with large lists.

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();
    }
}

CopyOnWriteArrayList Iterator Is Weakly Consistent

The iterator works on a snapshot of the array; modifications after iterator creation are invisible.

public Iterator<E> iterator() {
    return new COWIterator<>(getArray(), 0);
}
static final class COWIterator<E> implements ListIterator<E> {
    private final Object[] snapshot;
    private int cursor;
    // next(), hasNext() operate on snapshot only
}

Demo shows that changes made in another thread are not reflected when iterating the original snapshot.

CopyOnWriteArrayList Iterator Does Not Support Modification

public void remove() { throw new UnsupportedOperationException(); }
public void set(E e) { throw new UnsupportedOperationException(); }
public void add(E e) { throw new UnsupportedOperationException(); }

The iterator is read‑only.

Summary

Understanding the characteristics and hidden traps of Java collection classes—especially Arrays.asList, ArrayList, LinkedList, subList, and CopyOnWriteArrayList —helps developers choose the right container, avoid memory leaks, prevent unexpected exceptions, and achieve better performance.

Arrays.asList pitfalls
Arrays.asList pitfalls
subList deletion
subList deletion
subList OOM
subList OOM
Correct LinkedList usage
Correct LinkedList usage
List pitfalls mind map
List pitfalls mind map
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.

JavaCollectionsListArrayListCopyOnWriteArrayListLinkedList
Java Backend Technology
Written by

Java Backend Technology

Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!

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.