Course – LS – All

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE

1. Overview

In Java, when working with a List, sometimes, we may want to set a default value for elements in the list. For example, this can be useful when initializing a list.

In this tutorial, we’ll explore different approaches to setting default values for elements in a List in Java.

2. Using the Arrays.fill() 

First, let’s say we want to create a list with five String elements. Also, we want the string “new” to be the default value:

List<String> EXPECTED_LIST = Lists.newArrayList("new", "new", "new", "new", "new");

The standard API provides the Arrays.fill() method to fill the entire array with the same value. So, we can first initialize an array with a default value, then convert the array to a List using Arrays.asList().

Next, let’s implement the idea. For simplicity, we’ll use unit test assertions to verify if we can obtain the expected list from each approach:

String[] strings = new String[5];
Arrays.fill(strings, "new");
List<String> result = Arrays.asList(strings);
assertEquals(EXPECTED_LIST, result);

The implementation above is pretty straightforward. However, we should note that Arrays.asList() creates a list with a fixed size. If we attempt to add()/remove() elements to/from the list, it raises UnsupportedOperationException:

assertThrows(UnsupportedOperationException.class, () -> result.add("a new string"));
assertThrows(UnsupportedOperationException.class, () -> result.remove(0));

But we’re allowed to change the element’s value using the set() method, for example:

result.set(2, "a new value");
assertEquals("a new value", result.get(2));

3. Creating the newListWithDefault() Method

If we need to initialize a mutable list with default values, we can create a list object and then add default values. Of course, we can create a static method to do this job:

static <T> List<T> newListWithDefault(T value, int size) {
    List<T> list = new ArrayList<>(size);
    for (int i = 0; i < size; i++) {
        list.add(value);
    }
    return list;
}

As newListWithDefault() is a generic method, we can create lists of different types:

List<String> result = newListWithDefault("new", 5);
assertEquals(EXPECTED_LIST, result);

List<Integer> intList = newListWithDefault(42, 3);
assertEquals(Lists.newArrayList(42, 42, 42), intList);

Since newListWithDefault() returns a regular ArrayList, we can apply any changes to it, such as add(), remove(), and set().

4. What if the Elements Are Mutable?

Throughout our exploration, we have examined two methods for initializing a list with default values: using Arrays.fill(), leveraging our self-made newListWithDefault() method.

We have used the immutable type String for our list elements as examples for simplicity. But we should note that these methods behave differently when used with mutable values. In such cases where mutable objects are involved, all elements within the list will reference the same object. Consequently, modifying one element’s value will impact all other elements’ values.

Next, let’s take the Collections.nCopies() approach as an example to see the problem:

static final Date DATE_EPOCH = Date.from(Instant.EPOCH);
static final Date DATE_NOW = new Date();
List<Date> dateList = Collections.nCopies(2, Date.from(Instant.EPOCH));
assertEquals(Lists.newArrayList(DATE_EPOCH, DATE_EPOCH), dateList);
dateList.get(0).setTime(DATE_NOW.getTime());
assertEquals(Lists.newArrayList(DATE_NOW, DATE_NOW), dateList);

As we can see, first, we take Epoch as the default value and create a list of two Dates. Since all elements in the list reference the same object, modifying the value of one element leads to the alteration of values for all elements.

If we need the elements in the list to be different objects, we can change our newListWithDefault() method by replacing the default “T value” with a Supplier<T>:

static <T> List<T> newListWithDefault2(Supplier<T> supplier, int size) {
    List<T> list = new ArrayList<>(size);
    for (int i = 0; i < size; i++) {
        list.add(supplier.get());
    }
    return list;
}

Now, let’s initialize some lists using the newListWithDefault2() method and verify if it contains different objects:

List<String> result = newListWithDefault2(() -> "new", 5);
assertEquals(EXPECTED_LIST, result);

List<Date> dateList = newListWithDefault2(() -> Date.from(Instant.EPOCH), 2);
assertEquals(Lists.newArrayList(DATE_EPOCH, DATE_EPOCH), dateList);

dateList.get(0).setTime(DATE_NOW.getTime());
assertEquals(Lists.newArrayList(DATE_NOW, DATE_EPOCH), dateList);

As we can see from the test above, newListWithDefault2() takes a Supplier function as the parameter and produces a list with default values. Further, changing one element won’t impact others when the elements are mutable types.

5. Conclusion

In this article, we’ve explored various approaches to initialize a list with default values. Moreover, we’ve learned to use a Supplier function to provide the default value so that each element in the list references a different object.

As usual, all code snippets presented here are available over on GitHub.

Course – LS – All

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE
res – REST with Spring (eBook) (everywhere)
Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.