Back to Blog
Java

Java ArrayList set: Replacing Elements at an Index

java arraylist set: Learn how to replace an element in a Java ArrayList using the set() method, including its return value, bounds checking, and performance.

ArrayListJava Collectionsset methodindex-based accessJava List
Illustration of replacing an element in a Java ArrayList at a specific index using the set method

java arraylist set requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.

When you need to replace an element at a specific position in a Java ArrayList, the set(int index, E element) method is the direct way to do it. Unlike add, which inserts a new element and shifts subsequent elements, set overwrites the existing element at the given index without changing the list size. This operation is fundamental for in-place updates, such as correcting a value in a list of configuration entries or updating a row in an in-memory data structure.

The set(int index, E element) Method: Syntax and Behavior

The set method is part of the List interface and is implemented by ArrayList. Its signature is straightforward:

public E set(int index, E element)

It takes two arguments: the zero-based index where the replacement should occur, and the new element to store at that position. The method returns the element that was previously at that index. Here is a minimal example:

List<String> fruits = new ArrayList<>(); fruits.add("Apple"); fruits.add("Banana"); fruits.add("Cherry"); String old = fruits.set(1, "Blueberry"); System.out.println(old); // Banana System.out.println(fruits); // [Apple, Blueberry, Cherry]

The list size remains three. The element at index 1 changes from "Banana" to "Blueberry", and the old value is returned for any additional logic you may need.

What set Returns and Why It Matters

Because set returns the previous element, you can use it for operations that need the old value. For example, when maintaining a cache or tracking changes, you might want to log or compare the old value before replacing it.

Map<String, Integer> scoreMap = new HashMap<>(); List<Player> players = new ArrayList<>(); // ... populate players ... Player previous = players.set(0, new Player("Alice", 150)); if (previous != null) { System.out.println("Replaced " + previous.getName() + " with Alice"); }

If the list contains null elements, set can also return null, which is a valid previous value. This behavior is consistent with the List contract and does not indicate an error.

Handling Index Out of Bounds: IndexOutOfBoundsException

set throws IndexOutOfBoundsException if the index is out of range. The valid range is 0 through size() - 1. Trying to set an element at size() (which would be the position for an append) will fail, because set does not grow the list.

List<String> list = new ArrayList<>(); list.add("A"); list.set(1, "B"); // throws IndexOutOfBoundsException

Similarly, a negative index will always throw. The exception message includes the index and the current size, which helps with debugging:

Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 1, Size: 1

If you need to insert at a position that may be beyond the current size, use add(int index, E element) instead, which can grow the list and shift elements.

Using set vs add vs remove for Element Replacement

A common mistake is to replace an element by removing the old one and then adding the new one at the same index. This works but is inefficient and error-prone because remove shifts all subsequent elements left, and add shifts them right again. set avoids both shifts and directly writes to the backing array.

OperationEffect on SizeShifts ElementsReturns
set(index, e)UnchangedNoOld element
remove(index) + add(index, e)UnchangedYes (twice)Old element (from remove)
add(index, e)Increases by 1Yes (right)void

For a single replacement, set is the clear choice. The two-step approach also introduces a temporary state where the list has one fewer element, which can cause issues if other code observes the list between the operations.

Performance Characteristics of set on an ArrayList

ArrayList is backed by a plain Java array. The set method performs a bounds check and then assigns the new reference to the array slot. This is an O(1) operation, regardless of the list size. No elements are moved, and the list's internal modCount is incremented to support fail-fast iterators, but that does not affect the cost of the operation itself.

In contrast, add(int index, E element) is O(n) in the worst case because it must shift elements to the right. If you are doing many replacements at random indexes, set is the most efficient way to update values without resizing or shifting.

Common Mistakes and Edge Cases When Using set

One frequent error is using set on an empty list. Since the size is zero, any index is invalid. Always check size() before calling set if the list might be empty.

Another edge case is using set with an index that is equal to size() when you actually intend to append. This throws an exception, but the fix is simple: use add for appending.

Also note that set accepts null as the new element, just like add does. If your application forbids null values, you must validate before calling set.

Finally, be careful when using set in a loop that also modifies the list size. For example, if you remove elements while iterating and then call set with an index that was valid before the removal, you may get an IndexOutOfBoundsException. Always recalculate indexes after structural changes.

Practical Example: Updating Elements in a Loop

A common scenario is updating a list of mutable objects based on some condition. Here is a complete example that replaces every occurrence of a specific value:

List<Integer> numbers = new ArrayList<>(List.of(10, 20, 30, 20, 40)); for (int i = 0; i < numbers.size(); i++) { if (numbers.get(i) == 20) { numbers.set(i, 99); } } System.out.println(numbers); // [10, 99, 30, 99, 40]

Using set inside the loop is safe because the size does not change. The loop condition reads size() each iteration, but since set does not modify the size, the loop runs exactly the original number of times.

If you are using an enhanced for-loop, you cannot call set because it requires an index. In that case, use a traditional indexed loop or an ListIterator with its set method, which also works for ArrayList.

ListIterator<Integer> it = numbers.listIterator(); while (it.hasNext()) { if (it.next() == 20) { it.set(99); } }

The ListIterator.set method is another valid approach, but it only works when you are already iterating. For direct index access, ArrayList.set is the simplest and most readable option.

When set Is Not the Right Choice

If you need to insert an element without overwriting an existing one, use add. If you need to remove an element entirely, use remove. If you are frequently inserting or removing at the beginning of a large list, consider LinkedList, which has O(1) insertion and removal at the ends but O(n) access. However, LinkedList's set method is also O(n) because it must traverse to the index. For random access and replacement, ArrayList with set is the most efficient combination.

In concurrent scenarios, ArrayList is not thread-safe. If multiple threads may call set on the same list, you must synchronize externally or use a thread-safe list like CopyOnWriteArrayList. Note that CopyOnWriteArrayList.set has a different performance profile because it copies the entire array on every modification.

Understanding these tradeoffs helps you choose the right collection and method for your specific use case, ensuring both correctness and performance in production code.

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