ArrayList
ArrayList is the go‑to dynamic array implementation in Java’s collection framework.
It lives in java.util, extends AbstractList, and implements List, RandomAccess, Cloneable, and Serializable.
Unlike a fixed‑size array, an ArrayList grows automatically, so developers rarely need to pre‑allocate a specific capacity.
- Underlying storage – a contiguous
Object[]array. - Random access – retrieving an element by index is O(1).
- Nulls & duplicates – both are allowed.
- Order preservation – elements remain in insertion order, making it an ordered collection.
- Thread safety – not synchronized; external coordination is required in concurrent scenarios.
Basic Usage
Creation & Initialization
// Empty list; capacity is allocated lazily on first add
List<String> list = new ArrayList<>();
// Pre‑allocate to avoid repeated resizing
List<String> list2 = new ArrayList<>(100);
// Wrap an existing collection
List<String> list3 = new ArrayList<>(Arrays.asList("A", "B", "C"));
CRUD Operations
list.add("Java"); // Append at the end
list.add(0, "Hello"); // Insert at a specific index
String s = list.get(0); // Retrieve by index
list.set(0, "World"); // Replace element at index
list.remove(0); // Remove by index
list.remove("Java"); // Remove by value
int size = list.size(); // Current number of elements
Traversal Techniques
// Classic for loop
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
// Enhanced for loop (recommended)
for (String item : list) {
System.out.println(item);
}
// forEach with a lambda
list.forEach(System.out::println);
Expansion Strategy
The resizing algorithm is a key part of ArrayList’s design:
- Lazy allocation – the internal array starts empty; the first
addtriggers allocation of the default capacity (10). - Trigger point – when the element count exceeds the current capacity, a resize occurs.
- Growth factor – new capacity = old capacity + (old capacity >> 1) (i.e., 1.5×).
- Copying cost –
Arrays.copyOfcreates a new array and copies existing elements, incurring a performance hit.
Because resizing involves copying, it’s wise to specify an initial capacity when you can estimate the final size. For example, loading 10,000 records should use new ArrayList<>(10_000) to reduce the number of resizes and improve throughput.
ArrayList vs. LinkedList
| Feature | ArrayList | LinkedList |
|---|---|---|
| Structure | Dynamic array | Doubly‑linked list |
| Random access | O(1) | O(n) |
| Append at end | Amortized O(1) | O(1) |
| Insert/Remove in middle | O(n) | Locate O(n), operation O(1) |
| Memory overhead | Continuous block, possible unused space | Each node stores two pointers |
In practice, ArrayList dominates because of its cache friendliness and low per‑element overhead. LinkedList shines only when frequent insertions/removals at the ends are required and the list is large enough that the pointer overhead is acceptable.
Common Pitfalls & Best Practices
ConcurrentModificationException – modifying a list while iterating with a for‑each loop triggers a fail‑fast exception. Use an
Iteratorand itsremove()method, orlist.removeIf(predicate).Autoboxing ambiguity –
list.remove(1)on aList<Integer>removes the element at index 1, not the value 1. To delete by value, uselist.remove(Integer.valueOf(1)).subList is a view –
subList()returns a view backed by the original list. Changes to the view affect the parent list and vice versa.Thread safety – wrap with
Collections.synchronizedList(new ArrayList<>())or useCopyOnWriteArrayListfor read‑heavy, write‑light scenarios.Arrays.asList limitations – the list returned by
Arrays.asList()has a fixed size; attempts to add or remove elements throwUnsupportedOperationException. Convert to a mutable list first if mutation is needed.
Takeaway
ArrayList’s simplicity, efficient random access, and automatic resizing make it the default choice for most Java collections tasks. By understanding its underlying array mechanics, the 1.5× growth policy, and the subtle quirks that can trip developers, you can write code that is both performant and robust. When your workload involves frequent traversal or indexed access, ArrayList is almost always the optimal solution.