The method set(index, newElement) replaces an element in an ArrayList, as it overwrites the element at that index and returns the element that was there before. When we know the old value but not its position, we find the index with indexOf() first, and to change many elements at once we use replaceAll().
We replace elements whenever one item of a list changes and the rest stays as it is, such as a renamed task in a to-do app or a corrected price in an invoice. The list keeps its size, because a replacement only overwrites existing slots.
The following example replaces tasks in a to-do list by index, by value and all at once.
List<String> tasks = new ArrayList<>(List.of("email", "gym", "lunch", "call"));
String old = tasks.set(1, "yoga"); // "gym", tasks = [email, yoga, lunch, call]
int index = tasks.indexOf("lunch"); // 2
String replaced = tasks.set(index, "dinner"); // "lunch", tasks = [email, yoga, dinner, call]
boolean changed = Collections.replaceAll(tasks, "call", "meeting"); // true, tasks = [email, yoga, dinner, meeting]
tasks.replaceAll(String::toUpperCase); // [EMAIL, YOGA, DINNER, MEETING]
String bad = tasks.set(9, "gym"); // IndexOutOfBoundsException: Index 9 out of bounds for length 4
Notice that each set() call returns the old element, so we can log or undo the change, and that an index outside the list throws an exception instead of adding a new element. We start with set() and how it differs from add(), replace by value safely, replace many elements with Collections.replaceAll() and List.replaceAll(), and finish with lists of records and lists that reject changes.
1. Replacing an Element at an Index with set()
The method set(int index, E element) writes the new element into the slot at the index and returns the element it overwrote. It is declared in the List interface, so it works the same way on every modifiable list. A valid index goes from 0 to size() – 1.
For an ArrayList, set() runs in constant time, because the list keeps its elements in an array and writes the new element into one array slot. Nothing moves, and the size stays the same.
List<String> tasks = new ArrayList<>(List.of("email", "gym", "lunch", "call"));
String previous = tasks.set(3, "meeting"); // "call", tasks = [email, gym, lunch, meeting]
int size = tasks.size(); // 4
An index below 0 or at size() or above throws an IndexOutOfBoundsException. The message names the bad index and the list length, which helps when the index comes from a calculation.
List<String> tasks = new ArrayList<>(List.of("email", "gym", "lunch", "call"));
String tooBig = tasks.set(9, "yoga"); // IndexOutOfBoundsException: Index 9 out of bounds for length 4
String atSize = tasks.set(tasks.size(), "yoga"); // IndexOutOfBoundsException: Index 4 out of bounds for length 4
String negative = tasks.set(-1, "yoga"); // IndexOutOfBoundsException: Index -1 out of bounds for length 4
1.1. set() vs add(index, element)
Both methods take an index and an element, but they do different things. The method set() overwrites the element at the index, so the size stays the same. The method add(index, element) inserts the element and shifts the element at that position, and every element after it, one place to the right, so the size grows by one.

List<String> tasks = new ArrayList<>(List.of("email", "gym", "lunch"));
String old = tasks.set(1, "yoga"); // "gym", tasks = [email, yoga, lunch]
tasks.add(1, "coffee"); // [email, coffee, yoga, lunch]
The method set() never adds an element, so set(size(), element) throws an exception instead of appending. To append, we call add(element), and the add multiple elements to ArrayList article covers all add variants.
2. Replacing an Element by Value
To replace a value, we need its position first. The method indexOf() returns the index of the first element equal to the value, comparing with equals(), and set() overwrites that slot.
List<String> tasks = new ArrayList<>(List.of("email", "gym", "lunch", "call"));
int index = tasks.indexOf("lunch"); // 2
String old = tasks.set(index, "dinner"); // "lunch", tasks = [email, gym, dinner, call]
We can write both steps as one statement, but only when we are sure that the element is in the list. The method indexOf() returns -1 for a missing element, and set(-1, element) throws an exception.
List<String> tasks = new ArrayList<>(List.of("email", "gym", "dinner", "call"));
String oldCall = tasks.set(tasks.indexOf("call"), "meeting"); // "call", tasks = [email, gym, dinner, meeting]
String fails = tasks.set(tasks.indexOf("swim"), "yoga"); // IndexOutOfBoundsException: Index -1 out of bounds for length 4
For example, a to-do app gets a rename request for a task that another user deleted a second earlier. The one-line version throws an exception for that request, so the safe version checks the index first and reports whether the replacement happened.
static boolean replaceFirst(List<String> list, String oldValue, String newValue) {
int index = list.indexOf(oldValue);
if (index < 0) {
return false;
}
list.set(index, newValue);
return true;
}
List<String> tasks = new ArrayList<>(List.of("email", "gym", "dinner"));
boolean renamed = replaceFirst(tasks, "gym", "yoga"); // true, tasks = [email, yoga, dinner]
boolean missing = replaceFirst(tasks, "swim", "yoga"); // false, tasks = [email, yoga, dinner]
The method indexOf() also finds a null element, so the helper replaces null values too. To replace the last match instead of the first, we call lastIndexOf(), as the ArrayList contains() and indexOf() article shows.
3. Replacing Every Equal Value with Collections.replaceAll()
The method indexOf() finds only the first match. When a value occurs more than once, the static method Collections.replaceAll(list, oldValue, newValue) replaces every element that equals oldValue. It returns true when it replaced at least one element, or false when the list had no match.
List<String> week = new ArrayList<>(List.of("gym", "email", "gym"));
boolean changed = Collections.replaceAll(week, "gym", "yoga"); // true, week = [yoga, email, yoga]
boolean none = Collections.replaceAll(week, "swim", "run"); // false, week = [yoga, email, yoga]
The method compares with equals() and accepts null as the old or the new value, so Collections.replaceAll(list, null, “”) turns every null into an empty string.
4. Transforming Every Element with List.replaceAll()
When the new value depends on the old one, we call List.replaceAll(), added in Java 8. It takes a UnaryOperator<E>, which is a function that gets an element and returns a value of the same type. The method calls the function for every element and writes each result back into the same slot, and it returns nothing.
List<String> week = new ArrayList<>(List.of("gym", "email", "gym"));
week.replaceAll(String::toUpperCase); // [GYM, EMAIL, GYM]
week.replaceAll(t -> t.equals("GYM") ? "YOGA" : t); // [YOGA, EMAIL, YOGA]
Returning the element unchanged keeps it, as the second call shows for “EMAIL”. For longer logic, we store the operator in a variable with a clear name instead of writing a class that implements UnaryOperator, and chain operators with andThen().
List<String> titles = new ArrayList<>(List.of(" buy milk ", "call MOM"));
UnaryOperator<String> clean = t -> t.strip().toLowerCase();
titles.replaceAll(clean); // [buy milk, call mom]
For a List<Integer>, the operator works on the boxed values, so a price list can get a 10% markup in one call. The operator must not add or remove elements of the same list, because ArrayList.replaceAll() throws a ConcurrentModificationException when the list changes size during the call.
List<Integer> prices = new ArrayList<>(List.of(100, 250, 40));
prices.replaceAll(p -> p * 110 / 100); // [110, 275, 44]
Both replaceAll() methods change the list in place. A stream with map() puts the new values into a new list and leaves the original list as it was, which is the better choice when other code still reads the original list.
List<String> week = List.of("gym", "email");
List<String> upper = week.stream().map(String::toUpperCase).toList(); // [GYM, EMAIL]
5. Replacing an Object in a List by Its Id
In real apps, a list holds objects, and we find the element by a field such as an id. Say a to-do app keeps Task objects, and the user edits the title of the task with id 2. A Java record has no setters, so we build a new record and put it into the same slot with set().
record Task(int id, String title) {}
List<Task> tasks = new ArrayList<>(List.of(new Task(1, "email"), new Task(2, "gym")));
int index = IntStream.range(0, tasks.size()).filter(i -> tasks.get(i).id() == 2).findFirst().orElse(-1); // 1
Task old = index >= 0 ? tasks.set(index, new Task(2, "yoga")) : null; // Task[id=2, title=gym]
String current = tasks.toString(); // "[Task[id=1, title=email], Task[id=2, title=yoga]]"
The IntStream goes over the indexes instead of the elements, so the result is the position that set() needs. The check index >= 0 covers the case where no task has the id. When many tasks need the same change, replaceAll() with a condition does it in one pass.
List<Task> tasks = new ArrayList<>(List.of(new Task(1, "email"), new Task(2, "gym"), new Task(3, "gym")));
tasks.replaceAll(t -> t.title().equals("gym") ? new Task(t.id(), "yoga") : t);
long yoga = tasks.stream().filter(t -> t.title().equals("yoga")).count(); // 2
When the code loops over the list anyway, a ListIterator replaces the current element with its own set() method, without any index.
List<String> week = new ArrayList<>(List.of("yoga", "email", "yoga"));
ListIterator<String> it = week.listIterator();
while (it.hasNext()) {
if (it.next().equals("email")) {
it.set("calls");
}
}
String result = week.toString(); // "[yoga, calls, yoga]"
6. Replacing in Lists That Do Not Allow Changes
The method set() works on an ArrayList and on the fixed-size list from Arrays.asList(). Lists from List.of(), Stream.toList() or Collections.unmodifiableList() are unmodifiable, so set() and List.replaceAll() throw an UnsupportedOperationException. The method Collections.replaceAll() throws it as soon as it finds a matching element.
List<String> fixed = Arrays.asList("email", "gym");
String old = fixed.set(1, "yoga"); // "gym", fixed = [email, yoga]
List<String> constant = List.of("email", "gym");
String failed = constant.set(1, "yoga"); // UnsupportedOperationException
List<String> copy = new ArrayList<>(constant);
String inCopy = copy.set(1, "yoga"); // "gym", copy = [email, yoga]
To change an unmodifiable list, we copy it into a new ArrayList and replace the element in the copy, as the last two lines show. When we create the list ourselves, new ArrayList<>(List.of(…)) gives a modifiable list from the start, and the ArrayList guide covers the other ways to create one.
7. ArrayList Replace FAQs
Developers who replace list elements ask about size, loops, arrays and threads.
7.1. Does set() Change the Size of the List?
No. The method set() overwrites one existing element, so the size stays the same. Methods such as add() and remove() change the size, as we saw in section 1.1.
7.2. Can We Call set() Inside a for-each Loop?
Yes. Replacing an element is not a structural modification of an ArrayList, so the loop does not throw a ConcurrentModificationException. Calling add() or remove() in the same loop throws the exception. An index loop is clearer, because the index is at hand for set().
List<String> tasks = new ArrayList<>(List.of("email", "gym", "gym"));
for (int i = 0; i < tasks.size(); i++) {
if (tasks.get(i).equals("gym")) {
tasks.set(i, "yoga");
}
}
String result = tasks.toString(); // "[email, yoga, yoga]"
7.3. How Do We Replace an Element in a Plain Array?
We assign the new value to the index, as in names[1] = “yoga”;. An array has a fixed length, so an index outside the array throws an ArrayIndexOutOfBoundsException, which is a subclass of IndexOutOfBoundsException. The array vs ArrayList article compares both types.
7.4. Is set() Thread-Safe?
No. An ArrayList is not synchronized, so when several threads change the same list, we add synchronization ourselves. For a list shared between threads, we use Collections.synchronizedList() or a CopyOnWriteArrayList.
7.5. Which Is Faster, replaceAll() or a Loop with set()?
Both make one pass over the list and write each slot once, so the difference is small. We pick replaceAll() when every element goes through the same function, and an index loop when the replacement depends on the index or on neighboring elements.
8. Conclusion
The method set(index, element) replaces the element at a position, returns the old element and keeps the size of the list. To replace by value, we find the position with indexOf() and check for -1 before we call set().
For many elements, Collections.replaceAll() swaps every equal value, and List.replaceAll() passes each element through a function. Lists from List.of() reject both, so we replace elements in an ArrayList copy.
9. References
- ArrayList Javadoc (Java 25)
- List Javadoc (Java 25)
- Collections Javadoc (Java 25)
- UnaryOperator Javadoc (Java 25)
- ListIterator Javadoc (Java 25)
Happy Learning !!