Back to Blog
Java

Java ArrayList get: Syntax, Performance, and Edge Cases

java arraylist get: Learn how to use ArrayList.get() in Java, including syntax, performance, index bounds, and common pitfalls.

ArrayListJava CollectionsList InterfaceIndexOutOfBoundsExceptionJava Performance
A Java ArrayList with an index pointer highlighting the get() method retrieving an element from the array-backed list.

The get() method is the standard way to read an element from an ArrayList by its index. It is part of the List interface and is implemented by ArrayList with a straightforward array-backed lookup. In this article, we'll cover the exact syntax, the runtime behavior, the performance characteristics, and the edge cases you need to handle when using java arraylist get in real code.

The get() Method and Its Basic Syntax

ArrayList inherits get(int index) from the List interface. The method takes an integer position and returns the element at that position. The index is zero-based, so the first element is at index 0, and the last element is at size() - 1.

List<String> names = new ArrayList<>(); names.add("Alice"); names.add("Bob"); names.add("Carol"); String second = names.get(1); // returns "Bob"

The method signature is E get(int index) where E is the element type of the list. Because ArrayList is generic, the return type is the type parameter you declared when creating the list. This means you do not need to cast the result, which keeps the code type-safe.

How Index Bounds Are Checked

get() performs an explicit range check before returning the element. If the index is less than zero or greater than or equal to the current size, the method throws an IndexOutOfBoundsException. This is a runtime exception, so the compiler does not force you to catch it, but you should always be aware of the bounds when calling get().

List<Integer> numbers = new ArrayList<>(); numbers.add(10); numbers.add(20); int value = numbers.get(5); // throws IndexOutOfBoundsException

The exception message includes the actual index and the size, which helps during debugging. For example, it might say Index: 5, Size: 2. This check happens on every call, so it has a small cost, but it is negligible compared to the actual lookup.

Performance: Why get() Is O(1)

ArrayList is backed by a plain Java array. The get() method translates the logical index into a direct array position and returns the value stored there. This is a constant-time operation, often written as O(1). No traversal or search is required, regardless of how many elements the list contains.

This is a major advantage over linked-list-based implementations, such as LinkedList, where accessing an element by index requires walking the list from the head or tail. For ArrayList, the time to fetch the element does not depend on the size of the list. However, this constant-time behavior only applies to reads by index; insertion or removal in the middle of the list is still O(n) because elements must be shifted.

Comparing get() with Other Access Patterns

When you need a single element at a known position, get() is the most direct choice. But there are other ways to read elements, each with different tradeoffs.

ApproachTypical Use CasePerformance
list.get(i)Random access by indexO(1)
Enhanced for-eachIterate over all elementsO(n) total
list.iterator()Manual traversal with removalO(n) total
list.stream()Functional processingO(n) total

If you only need one element and you know its index, get() is the most efficient and readable option. If you need to process every element, a for-each loop or a stream is more idiomatic and avoids manual index management. Using get() inside a loop to iterate over the whole list is also O(n) total, but it adds the bounds check on each iteration and makes the code more verbose than necessary.

Common Pitfalls When Using get()

One frequent mistake is calling get() on an empty list or using an index that is out of range. This always throws IndexOutOfBoundsException. Another issue is assuming the index is based on insertion order when the list may have been modified. For example, if you remove an element, the indices of all subsequent elements shift down by one. Code that stored an index earlier may now point to a different element or be out of bounds.

A subtler problem occurs when you mix get() with manual size checks. The size of an ArrayList can change between the check and the call if the list is modified by another thread. This is a concurrency concern, covered in the next section.

Using get() in Loops and When to Avoid It

It is common to see code that iterates with an index and calls get() for each position:

for (int i = 0; i < list.size(); i++) { process(list.get(i)); }

This works, but it is less readable than an enhanced for-each loop:

for (String item : list) { process(item); }

The for-each loop uses an iterator internally and avoids the explicit index and bounds check. There is no performance advantage to using get() in this pattern; both are O(n). However, there are cases where you need the index, such as when you want to compare adjacent elements or modify the list during iteration. In those cases, get() is necessary.

If you need to remove elements while iterating, use an Iterator or the removeIf method, because calling get() and then remove() by index can lead to skipped elements or IndexOutOfBoundsException if you do not adjust the index carefully.

Concurrency and Modification Concerns

ArrayList is not thread-safe. If one thread modifies the list while another thread calls get(), the behavior is undefined. The get() method itself is a simple read, but the size and the backing array can change concurrently, leading to stale reads or exceptions. For concurrent access, use a CopyOnWriteArrayList or synchronize externally.

Even in single-threaded code, you must be careful when the list is modified after you obtain an index. For example, if you store an index and then remove an element, the stored index may no longer point to the intended element. This is not a problem with get() itself but with the assumption that indices remain stable.

A practical rule is to use get() when you have a stable index and a list that is not being structurally modified. If you need to access elements while the list is changing, consider using an iterator or a different collection type.

When get() Is Not the Right Choice

If your code frequently needs to access elements by index and also frequently inserts or removes elements at arbitrary positions, ArrayList may not be the best data structure. The O(1) read advantage is offset by the O(n) shift cost of insertion and removal. In such scenarios, a LinkedList might be better for modifications, but its get() is O(n). Alternatively, a HashMap or a sorted structure might be more appropriate if you are looking up by a key rather than an index.

Also, if you are working with primitive types like int or double, ArrayList stores boxed objects (Integer, Double), which adds memory overhead and can cause autoboxing cost when calling get(). For performance-critical numeric code, consider using an array or a specialized library like Trove or Eclipse Collections.

Finally, remember that get() returns a reference to the object stored in the list. If the object is mutable, modifying it through the returned reference affects the list's content. This is often intended, but it can lead to surprising behavior if you forget that the list holds references, not copies.

java arraylist get: Practical Usage and Code Examples | RYUSLOG DEV