Fundamentals 14 min read

Deep Dive into Java ArrayList: How It Works Under the Hood

This article explores Java's ArrayList implementation in depth, covering its core interfaces, member variables, constructors, and the most commonly used methods, while illustrating how the internal array expands and how each operation manipulates the underlying data structure.

Java Backend Technology
Java Backend Technology
Java Backend Technology
Deep Dive into Java ArrayList: How It Works Under the Hood

Overall Introduction

The Collection interface is the root of Java's collection hierarchy; it has no direct implementation, while Set and List extend it. A Set contains no duplicate elements and has no guaranteed order, whereas a List is ordered and may contain duplicates. Map is a separate interface that stores key‑value pairs, where keys must be unique but values may repeat.

Collection vs List vs Set vs Map diagram
Collection vs List vs Set vs Map diagram

Member Variables

ArrayList defines several key fields: DEFAULT_CAPACITY – the default initial capacity (10). EMPTY_ELEMENTDATA – a shared empty Object[] used when the list is created without a specified capacity. elementData – the internal Object[] that actually stores the elements. size – the number of elements currently stored in elementData, not the array length.

ArrayList member variables diagram
ArrayList member variables diagram

Constructors

ArrayList provides three constructors: ArrayList(int initialCapacity) – creates an internal array of the given size (or uses EMPTY_ELEMENTDATA if the size is zero). Negative values throw IllegalArgumentException. ArrayList() – uses DEFAULT_CAPACITY_EMPTY_ELEMENTDATA (effectively a capacity of 10). ArrayList(Collection<? extends E> c) – copies the elements of the supplied collection into a new internal array via c.toArray(), handling empty collections and type conversion as needed.

ArrayList constructors diagram
ArrayList constructors diagram

Common Methods

size()

Returns the current size field, i.e., the number of stored elements.

isEmpty()

Checks whether size == 0, indicating that the internal array holds no elements.

add(E e)

Appends an element to the end of the list. It first ensures sufficient capacity by calling ensureExplicitCapacity, which may trigger grow() if the array is full. After capacity is guaranteed, the element is stored at elementData[size++].

add(E) implementation
add(E) implementation

The capacity‑ensuring sequence involves:

Incrementing modCount (a modification counter from AbstractList).

Calculating the required minimum capacity and comparing it with elementData.length.

Calling grow() when needed.

ensureExplicitCapacity
ensureExplicitCapacity
grow() implementation
grow() implementation

add(int index, E element)

Inserts an element at the specified position, shifting subsequent elements one slot to the right using System.arraycopy. Bounds are checked, capacity is ensured, and modCount is updated.

clear()

Increments modCount, nulls out all entries in the internal array, and resets size to zero.

clear() implementation
clear() implementation

remove(int index)

Validates the index, increments modCount, retrieves the element to return, shifts following elements left with System.arraycopy, decrements size, and nulls the now‑vacant slot.

remove(int) implementation
remove(int) implementation

remove(Object o)

If o is null, the method scans for the first null entry and calls fastRemove (a streamlined version without bounds checking). Otherwise it searches for a matching element, removes it via the same shifting logic, and returns true if removal succeeded.

remove(Object) implementation
remove(Object) implementation

contains(Object o)

Delegates to indexOf(o) != -1. The indexOf method iterates over elementData, handling null specially, and returns the first matching index or -1 if not found.

contains() implementation
contains() implementation

get(int index)

Performs a bounds check and returns elementData[index].

get() implementation
get() implementation

set(int index, E element)

Similar to add(int, E) but overwrites the existing value at index without shifting other elements.

set() implementation
set() implementation

Conclusion

ArrayList is backed by a dynamically resized array; when the capacity is exceeded, it grows by roughly 1.5× in Java 8.

Array‑based storage gives fast random access but makes deletions and insertions costly due to element shifting and occasional array copying. Pre‑sizing the list when the expected size is known can avoid frequent reallocations.

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.

JavaData StructuresArrayListsource code
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.