add()
Appends an element or inserts it at a position, and explains why add sometimes throws instead of growing the list.
boolean add(E e)
void add(int index, E element)- Added in Java Java 1.2
Parameters
| Parameter | Type | Description |
|---|---|---|
$e | E | The element to append to the end of the list. |
$indexoptional | int | Used by the add(int, E) overload: the position to insert at, from 0 to size() inclusive. Later elements shift one place right. |
Return value
booleantrue when the element was added. A List always returns true; the indexed overload returns nothing.
Errors
UnsupportedOperationException if the list is unmodifiable or fixed-size (List.of, Arrays.asList). IndexOutOfBoundsException from add(int, E) when index < 0 or index > size(). NullPointerException if the list does not accept nulls. ClassCastException or IllegalArgumentException if the element cannot be stored.
Changelog
- PHP Java 21List became a SequencedCollection and gained addFirst(E) and addLast(E), which behave like add(0, e) and add(e) on a modifiable list.
add(e) appends to the end of the list. add(index, e) inserts at a position and shifts every later element one place to the right. These are the two ways a list grows, and most code only needs the first.
Cost on ArrayList
Appending to an ArrayList is constant time on average: the backing array occasionally has to grow, which copies it, but that cost is spread across many adds. Inserting near the front is linear, because every later element moves. If you are building a list of 10,000 rows by inserting at index 0, you are copying the array 10,000 times. Append and then call reversed() or sort instead.
When you know how many elements are coming, such as mapping a page of database rows, new ArrayList<>(rows.size()) sizes the array once.
Why add throws
UnsupportedOperationException: the list is unmodifiable (List.of,List.copyOf,Stream.toList(),Collections.unmodifiableList) or fixed-size (Arrays.asList). Copy intonew ArrayList<>(...)first.IndexOutOfBoundsException: the index is negative or greater thansize(). Inserting at exactlysize()is allowed and means append.ConcurrentModificationException: you added to the list while a for-each loop over it was running. Collect new items in a second list andaddAllthem afterwards.
The boolean return value comes from Collection, where a Set may refuse a duplicate. For a List it is always true, so there is nothing useful to check.
Related methods
addAll(collection) appends many elements in one call, and addAll(index, collection) inserts them at a position. Since Java 21, addFirst(e) and addLast(e) say the intent more plainly than add(0, e).
In a web service a plain ArrayList is not safe to add to from several request threads at once. Keep lists local to a request, or use a concurrent structure for shared state.
Example
When to use it
- Accumulating validation messages for a single 400 Bad Request response.
- Mapping database rows into DTOs by appending one record per row.
- Building an ordered pipeline of request handlers or filters at start-up.
Frequently asked questions
How do I add an element to the beginning of a list in Java?
Call list.add(0, element), or list.addFirst(element) on Java 21 and later. On an ArrayList this shifts every element, so for frequent front inserts use an ArrayDeque instead.
Why does list.add throw UnsupportedOperationException?
The list cannot grow: it came from List.of, List.copyOf, Stream.toList, Collections.unmodifiableList or Arrays.asList. Create a mutable copy with new ArrayList<>(list) and add to that.
More examples
Fixed-size lists refuse add
Arrays.asList allows set but not add or remove, because its size is tied to the array. Copying into an ArrayList gives a list that can grow.
void main() {
List<String> fromArray = Arrays.asList("a", "b");
try {
fromArray.add("c");
} catch (UnsupportedOperationException e) {
IO.println("Arrays.asList is fixed-size");
}
List<String> editable = new ArrayList<>(fromArray);
editable.add("c");
IO.println(editable);
}
// Output:
// Arrays.asList is fixed-size
// [a, b, c]Collecting validation errors
Each failed rule appends one message, so the client gets every problem in a single 400 response instead of one at a time.
record SignUp(String email, int age) {}
List<String> validate(SignUp form) {
List<String> errors = new ArrayList<>();
if (form.email() == null || form.email().isBlank()) {
errors.add("email is required");
}
if (form.age() < 18) {
errors.add("age must be at least 18");
}
return errors;
}
void main() {
IO.println(validate(new SignUp("", 16)));
IO.println(validate(new SignUp("[email protected]", 30)));
}
// Output:
// [email is required, age must be at least 18]
// []Adding while iterating
The for-each iterator notices the list changed under it and fails fast. Gathering new items separately and calling addAll after the loop avoids the problem.
void main() {
List<String> jobs = new ArrayList<>(List.of("a", "b"));
try {
for (String job : jobs) {
if (job.equals("a")) {
jobs.add("a-retry");
}
}
} catch (ConcurrentModificationException e) {
IO.println("ConcurrentModificationException");
}
List<String> queue = new ArrayList<>(List.of("a", "b"));
List<String> retries = new ArrayList<>();
for (String job : queue) {
if (job.equals("a")) {
retries.add(job + "-retry");
}
}
queue.addAll(retries);
IO.println(queue);
}
// Output:
// ConcurrentModificationException
// [a, b, a-retry]
Discussion