Java ArrayList size: Using size() Correctly
java arraylist size: Learn how to get the number of elements in a Java ArrayList using size(), its O(1) behavior, and how it differs from capacity and length.
The java arraylist size is obtained with the size() method, which returns the number of elements currently stored in the list. This method is O(1) because ArrayList maintains an internal size field that is updated on every add and remove operation. Understanding exactly what size() returns and what it does not can prevent common bugs in collection handling.
How to Call size() on an ArrayList
Calling size() is straightforward. You invoke it on an ArrayList instance and receive an int value representing the element count.
import java.util.ArrayList; ArrayList<String> names = new ArrayList<>(); names.add("Alice"); names.add("Bob"); names.add("Carol"); int count = names.size(); System.out.println(count); // 3
The method takes no arguments and returns the current number of elements. It does not require any iteration or computation beyond reading a field, so it is safe to call repeatedly in loops and conditions.
What size() Returns and What It Does Not
size() returns the number of elements that have been added to the list and have not yet been removed. It does not return the capacity of the internal array, which is the maximum number of elements the list can hold before it needs to grow. The capacity is not directly exposed by the public API, though you can infer it indirectly through reflection or by observing memory usage.
It also does not return the length of the underlying array. The internal array may be larger than the element count to allow for future additions without reallocation. For example, after adding a few elements, the internal array might have capacity 10 while size() returns 3.
size() vs length vs capacity
The distinction between size, length, and capacity is a frequent source of confusion, especially for developers coming from array-based languages.
| Concept | Applies To | Meaning | Example |
|---|---|---|---|
size() | ArrayList | Number of elements currently in the list | list.size() returns 3 |
length | Arrays | Number of elements the array can hold (fixed) | array.length returns 10 |
capacity | ArrayList (internal) | Number of elements the list can hold without resizing | Not directly accessible |
An array's length is a property, not a method, and it is fixed at creation. An ArrayList grows automatically, so its capacity changes over time. size() is the only reliable way to know how many elements are logically present.
Performance and Runtime Behavior of size()
The size() method runs in constant time, O(1). It simply returns the value of an internal int field that is updated whenever the list is modified. This makes it safe to call in performance-sensitive code, such as loop conditions or frequent checks.
Because size() does not iterate over the elements, its cost does not increase with the number of elements. This is different from methods like contains() or indexOf(), which scan the list and run in O(n).
There is no hidden allocation or locking in size(). It is a simple getter, so it is suitable for use in multi-threaded contexts only if the list itself is properly synchronized. The method itself is thread-safe in the sense that it does not modify state, but the value it returns may be stale if another thread is concurrently modifying the list.
Common Mistakes When Checking ArrayList Size
One common mistake is using the length property on an ArrayList. This will not compile because ArrayList does not have a length field. For example:
ArrayList<String> list = new ArrayList<>(); int size = list.length; // compilation error
Another mistake is confusing size() with capacity. If you pre-allocate an ArrayList with an initial capacity, size() still returns 0 until elements are added:
ArrayList<String> list = new ArrayList<>(100); System.out.println(list.size()); // 0, not 100
This often surprises developers who expect the initial capacity to represent the number of elements. The capacity only affects internal memory allocation, not the logical size.
Off-by-one errors can occur when using size() in loops. For example, iterating with an index from 0 to size() inclusive will cause an IndexOutOfBoundsException because the valid indices are 0 through size() - 1.
Using size() in Loops and Conditions
Because size() is O(1), it is safe to call it directly in a loop condition:
for (int i = 0; i < list.size(); i++) { // process list.get(i) }
There is no performance penalty from calling size() on every iteration. However, if the list is modified during the loop, the condition may change, leading to skipped or repeated elements. This is a general issue with any collection that is modified while iterating, not specific to size().
For read-only iteration, the enhanced for loop is often clearer:
for (String item : list) { // process item }
The enhanced loop internally uses an iterator and avoids index arithmetic, but it still relies on the list's size to determine when to stop.
Thread Safety and Size Consistency
ArrayList is not thread-safe. If one thread calls size() while another thread adds or removes elements, the returned value may be inconsistent. The size() method itself does not synchronize, so it can return a value that does not reflect the current state at the exact moment of the call.
For concurrent access, use Collections.synchronizedList() or a concurrent collection like CopyOnWriteArrayList. Even with a synchronized list, the size() call is atomic, but the overall operation of checking size and then acting on the list may still require external synchronization to avoid race conditions.
In practice, relying on size() for decisions in a multi-threaded environment without proper synchronization can lead to subtle bugs. For example:
if (list.size() > 0) { String first = list.get(0); // may fail if another thread removes the element }
This check-then-act pattern is not safe with ArrayList. Consider using a concurrent collection or synchronizing the block that checks and acts.
When to Use size() vs isEmpty()
For checking whether a list has no elements, isEmpty() is more expressive than size() == 0. Both are O(1), but isEmpty() communicates intent more clearly:
if (list.isEmpty()) { // handle empty list }
Using size() == 0 is not wrong, but it adds a comparison that is unnecessary when the goal is simply to test for emptiness. Prefer isEmpty() for readability, and use size() when you need the exact count for other logic.
There is no performance difference between the two, so the choice is purely about code clarity.
Edge Cases: Empty and Null Lists
An ArrayList that has been instantiated but not yet populated has a size of 0. Calling size() on such a list is safe and returns 0.
A null reference, however, will throw a NullPointerException when you call size():
ArrayList<String> list = null; int size = list.size(); // NullPointerException
Always ensure the list is initialized before calling size(). If you are unsure whether a variable might be null, check for null first or use Optional to handle the absence of a list.
In library code that accepts a list as a parameter, you can defensively check for null and treat it as empty if the contract allows:
public int getCount(ArrayList<String> list) { return (list == null) ? 0 : list.size(); }
This pattern avoids unexpected exceptions when the caller passes a null reference.
The size() method is a fundamental part of the List interface, and its behavior is consistent across all implementations, including LinkedList and Vector. Understanding what it returns and how it behaves under different conditions will help you write more reliable Java code.