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.
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()); // 1The 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); // throwsThe 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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
