Back to Blog
Java

Java Collections fill: Replacing List Elements

Learn how to use Java's Collections.fill to replace all list elements with a single value, including behavior, pitfalls, and performance.

java collectionsfill methodlist manipulationjava utilarrays fill
Illustration of Java Collections.fill replacing list elements with a single value

The static method Collections.fill(List<? super T> list, T obj) replaces every element in the specified list with the given object. It is a simple utility for resetting or reinitializing a list's contents without changing its size or structure. For example, if you have a list of integers and want to set every element to zero, you can do this in one call.

List<Integer> scores = new ArrayList<>(Arrays.asList(10, 20, 30, 40)); Collections.fill(scores, 0); System.out.println(scores); // [0, 0, 0, 0]

The method operates on the List interface, so it works with any implementation such as ArrayList, LinkedList, or a custom list. It does not return a new list; it modifies the list in place. The type parameter T is the value type, and the list is declared as List<? super T> to allow filling a list of a supertype with a subtype value, which is a common generics pattern.

How Collections.fill Works

The public signature is:

public static <T> void fill(List<? super T> list, T obj)

Behaviorally, the method replaces every current element with obj. For RandomAccess lists such as ArrayList, it can use index-based set calls. For lists without efficient random access, such as a larger LinkedList, it avoids repeated index lookup by advancing with a ListIterator and calling set on the current element. In both cases the operation makes one pass over the list.

The method requires that the list supports set operations. For unmodifiable lists, such as those returned by Collections.unmodifiableList, it throws an UnsupportedOperationException. It also throws a NullPointerException if the list is null. The value obj can be null. For list implementations that permit null elements, every element becomes null; this is often used to release references.

Using Collections.fill with ArrayList and LinkedList

Because fill works on the List interface, it behaves consistently across different implementations. With an ArrayList, set is backed by direct array indexing, so the fill is fast. With a LinkedList, calling set(index, value) directly in a loop would require traversing to that index for each call, which would be slow if done repeatedly. For larger linked lists, Collections.fill avoids that by advancing through the list with a ListIterator and updating the current node without an index lookup. The method makes a single pass over the list for standard implementations such as ArrayList and LinkedList, so the overall work is linear in the list size.

List<String> names = new LinkedList<>(Arrays.asList("Alice", "Bob", "Carol")); Collections.fill(names, "Unknown"); System.out.println(names); // [Unknown, Unknown, Unknown]

In practice, the performance difference between ArrayList and LinkedList for fill is small for typical list sizes. It is more relevant when a manual index-based loop would otherwise cause repeated traversal on a non-random-access list. The list-iterator path is the reason fill stays efficient on classes such as LinkedList.

What Collections.fill Does Not Do

It is important to understand that Collections.fill does not add elements to the list. If the list is empty, calling fill has no effect because there are no elements to replace. It does not resize the list or insert new elements. If you need a new list that contains repeated values, you can use Collections.nCopies with a constructor. For example, to create a list of five null values, you cannot use fill on an empty list; instead, you would write:

List<String> list = new ArrayList<>(Collections.nCopies(5, null));

If you need to add repeated values to an existing list, use a loop or addAll(Collections.nCopies(...)). The fill method only modifies existing elements.

Performance and Memory Behavior

For the standard List implementations, the time complexity of Collections.fill is O(n), where n is the number of elements in the list. It performs one set operation per element and makes a single pass over the list. No list-sized temporary storage is allocated, so memory overhead is constant. This makes fill an efficient way to reset a list when you need to reuse the same list object rather than creating a new one.

Creating a new list with repeated values, such as using Collections.nCopies and passing it to a constructor, also runs in O(n) time but allocates a new list and may copy references. If you need to preserve the original list identity—for example, because other objects hold a reference to it—fill is the better choice. If you can discard the old list, building a new one might be clearer.

Common Pitfalls and Edge Cases

Several edge cases can cause unexpected behavior or exceptions:

  • Unmodifiable lists: Collections.fill throws UnsupportedOperationException if the list does not support set. This includes lists created with Collections.unmodifiableList or List.of (Java 9+).
  • Fixed-size lists: Arrays.asList returns a fixed-size list backed by an array. It supports set, so fill works, but you cannot add or remove elements. fill only replaces, so it is safe.
  • Null values: Passing null as the value sets every element to null in list implementations that permit null elements. This is often used to release references, but be aware that it may cause NullPointerException later if the list is used without null checks. Some collections also reject null elements and may throw when fill tries to store null.
  • Concurrent modification: Collections.fill is not thread-safe. If the list is shared across threads, synchronize access or use a Collections.synchronizedList wrapper; otherwise concurrent modification could cause exceptions or inconsistent state.
  • Empty list: As mentioned, fill does nothing on an empty list. It does not throw an error; it simply iterates zero times.

These edge cases are not obscure; they are common in production code, especially when dealing with immutable collections or shared data structures.

Alternatives to Collections.fill

For arrays, the java.util.Arrays class provides a similar method: Arrays.fill. It works on arrays of primitive types and objects. For example:

int[] arr = new int[5]; Arrays.fill(arr, 42);

For lists, you can also use a simple loop. For random-access lists such as ArrayList, the following is functionally equivalent to Collections.fill, but the loop gives you more control, such as conditionally setting values based on index:

for (int i = 0; i < list.size(); i++) { list.set(i, value); }

For non-random-access lists, prefer an iterator-based loop if you need manual control, or simply use Collections.fill.

Java streams offer another way, but they are not designed for in-place modification; you would typically create a new list:

List<Integer> newList = list.stream().map(x -> 0).collect(Collectors.toList());

This is less efficient and changes the list identity, so it is not a direct replacement.

Choosing Between Collections.fill and Manual Loops

Use Collections.fill when you need to set every element to the same value and the list is mutable and supports set. It is concise, self-documenting, and avoids off-by-one errors. Use a manual loop when you need to apply different values based on the index or when you need to perform additional logic during the iteration. For example, if you want to fill an ArrayList with a sequence of numbers, a loop is necessary:

for (int i = 0; i < list.size(); i++) { list.set(i, i * 10); }

For primitive arrays, Arrays.fill is the direct equivalent and should be preferred over a loop for readability. For immutable lists, neither fill nor a loop works; you must create a new list. The decision ultimately comes down to whether the operation is a uniform replacement (use fill) or a computed replacement (use a loop).

When performance is critical, Collections.fill is as efficient as a carefully written loop: it avoids per-element index traversal on large linked lists and uses ordinary set operations on RandomAccess lists. Avoid replacing it with an index-based loop for non-random-access lists such as LinkedList, because each set(index, value) call would require traversal. In concurrent scenarios, you must synchronize the list regardless of which approach you choose. In practice, Collections.fill is the clearest way to express the intent of resetting a list, and it is the idiomatic Java approach for this operation.

Java Collections.fill: Examples, Pitfalls, and Performance | RYUSLOG DEV