Core Java

Add Elements to an Immutable List

1. Introduction

In Java, immutable objects are designed to have their state remain unchanged throughout their lifetime to ensure thread safety and prevent unintended modifications, fostering robust and reliable code. However, there are scenarios where applications need to create a modified version of an immutable object. In this example, I’ll demonstrate how to modify an immutable list by adding elements.

Here are some common use cases that need to modify immutable objects:

Use CaseDescription
Thread SafetyIn concurrent environments, we may need to create a new immutable object with updated data when certain changes are applied atomically.
CachingNeed to create a new immutable object with updated data and replace the old cached object with the new one when a cache eviction or refresh occurs.
Value ObjectsNeed to create new objects with the results of the operations to avoid side effects when performing operations on value objects such as dates, currencies, and measurements.
Functional programmingJava functions typically create new immutable objects as output based on input parameters, rather than modifying existing objects. e.g. Streams API.
Table 1. Modify Immutable Objects Use Cases

2. Set up Maven Project

In this step, I will set up a Java maven project which demonstrates immutable list add element via Junit tests.

pom.xml

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.zheng</groupId>
    <artifactId>Immutable-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <dependencies>
        <!--
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.10.2</version>
            <scope>test</scope>
        </dependency>
 
    </dependencies>
</project>

I will create a TestAddElementToImmutaleList class with several tests to show how to create a new list from an immutable list and convert the modified list into a new immutable list. Each method will be explained at step 3 and step 4.

TestAddElementToImmutaleList.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
package org.zheng.demo;
 
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
 
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
 
import org.junit.jupiter.api.Test;
 
class TestAddElementToImmutaleList {
    final List<Integer> oldImmutableList = List.of(1, 2);
    List<Integer> newImmutableList;
 
    @Test
    void test_addAll_after_new_arrayList() {
        final List<Integer> newList = new ArrayList<>();
        newList.addAll(oldImmutableList);
        newList.add(3);
 
        assertEquals(3, newList.size(), "newList should have 3 elements after add");
        createImmutableList(newList);
 
    }
 
    private void createImmutableList(final List<Integer> newList) {
        newImmutableList = newList.stream().toList();
 
        newImmutableList.stream().forEach(n -> System.out.print(n + " "));
        System.out.println("***");
    }
 
    @Test
    void test_add_after_collector_toList() {
        final List<Integer> newList = oldImmutableList.stream().collect(Collectors.toList());
 
        newList.add(3);
 
        assertEquals(3, newList.size(), "newList should have 3 elements after add");
        createImmutableList(newList);
    }
 
    @Test
    void test_add_after_ArrayList_constructor() {
        final List<Integer> newList = new ArrayList<>(oldImmutableList);
        assertEquals(2, newList.size(), "newList should have 2 elements");
 
        newList.add(3);
 
        assertEquals(3, newList.size(), "newList should have 3 elements after add");
        createImmutableList(newList);
    }
 
    @Test
    void test_add_after_toArray() {
        Integer[] newArray = oldImmutableList.toArray(new Integer[3]);
        final List<Integer> newList = Arrays.asList(newArray);
 
        newList.set(2, 3);
 
        assertEquals(3, newList.size(), "newList should have 3 elements");
        createImmutableList(newList);
    }
 
    @Test
    void test_modify_immutable_throw_exception() {
        assertThrows(UnsupportedOperationException.class, () -> {
            oldImmutableList.set(0, 4);
        }, "should throw exception");
 
        final List<Integer> newList = oldImmutableList.stream().toList();
        assertThrows(UnsupportedOperationException.class, () -> {
            newList.add(2);
        }, "should throw exception");
    }
 
}

3. Create an Immutable List

In this step, I will create an immutable list from the Stream.toList method.

3.1 Create an Immutable List

In this step, I will create an immutable list – immutableList – with the List.of method which returns an unmodifiable list containing two integers: 1 and 2. I will also define a newImmutableList which will be used in later steps to add one more integer to contain 3 integers: 1, 2, and 3.

define immutableList and newImmutableList

1
2
final List<Integer> oldImmutableList= List.of(1, 2);
List<Integer> newImmutableList;

Note: the immutable list can NOT be modified by changing the existing value nor adding new values. You can verify with the following test.

Can not modify immutableList

01
02
03
04
05
06
07
08
09
10
11
12
@Test
void test_modify_immutable_throw_exception() {
 
    assertThrows(UnsupportedOperationException.class, () -> {
        immutableList.set(0, 4);
    }, "should throw exception");
 
    List<Integer> newList = immutableList.stream().toList();
    assertThrows(UnsupportedOperationException.class, () -> {
        newList.add(2);
    }, "should throw exception");
}

3.2 Create an Immutable List via Stream.toList

In this step, I will use the Stream.toList method to return an immutable list. It is used to assign the newImmutableList variable created at step 3.1.

create an immutable List

1
2
3
4
5
private void createImmutableList(final List<Integer> newList) {
    newImmutableList = newList.stream().toList();
 
    newImmutableList.stream().forEach(n -> System.out.print(n + " "));
}
  • line 2: create a new immutable list from is a mutable newList.
  • line 4: print out the newImmutableList values.

4. Modify an Immutable List

In this step, I will demonstrate immutable list add element with three steps:

  • create a new mutable list by copying from the immutable list.
  • add new elements to the mutable list.
  • create a new immutable list by calling the createImmutableList created at step 3.2.

4.1 Create a list via ArrayList

I will add one element after constructing a new mutable ArrayList.

test_addAll_after_new_arrayList

01
02
03
04
05
06
07
08
09
10
@Test
    void test_addAll_after_new_arrayList() {
        final List<Integer> newList = new ArrayList<>();
        newList.addAll(oldImmutableList);
        newList.add(3);
         
        assertEquals(3, newList.size(), "newList should have 3 elements after add");
        createImmutableList(newList);
 
    }
  • line 3: create a new mutable list via the default ArrayList constructor.
  • line 4: utilize the addAll method to copy the oldImmutableList elements to the newList.
  • line 5: add an element to the newList.
  • line 8: call createImmutableList to return a new immutable list.

4.2 Create List via Arrays

I will add one element after constructing a new mutable list from Arrays.asList.

test_add_after_toArray

01
02
03
04
05
06
07
08
09
10
@Test
    void test_add_after_toArray() {
        Integer[] newArray = oldImmutableList.toArray(new Integer[3]);
        final List<Integer> newList = Arrays.asList(newArray);
             
        newList.set(2, 3);
         
        assertEquals(3, newList.size(), "newList should have 3 elements");
        createImmutableList(newList);
    }
  • line 3: create a new Integer Array with size 3, one bigger than the oldImmutableList.size.
  • line 4: create a new list from Arrays.asList.
  • line 6: update the newList to set the third element value to 3.

4.3 Create List via ArrayList Copy Constructor

I will add one element after constructing a new mutable ArrayList.

test_add_after_ArrayList_constructor

01
02
03
04
05
06
07
08
09
10
@Test
    void test_add_after_ArrayList_constructor() {
        final List<Integer> newList = new ArrayList<>(oldImmutableList);
        assertEquals(2, newList.size(), "newList should have 2 elements");
 
        newList.add(3);
         
        assertEquals(3, newList.size(), "newList should have 3 elements after add");
        createImmutableList(newList);
    }
  • line 3: create a new ArrayList with the oldImmutableList.
  • line 6: add a new element to the newlist.

4.4 Create List via Collectors.toList

I will add one element after constructing a new mutable from Collectors.toList.

test_add_after_collector_toList

1
2
3
4
5
6
7
8
@Test
void test_add_after_collector_toList() {
    final List<Integer> newList = oldImmutableList.stream().collect(Collectors.toList());
 
    newList.add(3);
    assertEquals(3, newList.size(), "newList should have 3 elements after add");
    createImmutableList(newList);
}
  • line 3: create a newList from the Collectors.toList method.
  • line 5: add a new element to the newList.

5. Conclusion

In this example, I demonstrated how to modify an immutable list with three steps:

  • create a new mutable list by copying from an immutable list.
  • add new elements to the mutable list.
  • return an immutable list via the Stream.toList method.

Here is the Junit test execution screenshot. As you see in the Console log, the new immutable list has three integers: 1, 2, and 3.

Figure 1. Junit Test Results

6. Download

This was an example of an immutable list add element in Java.

Download
You can download the full source code of this example here: Add Elements to an Immutable List
Do you want to know how to develop your skillset to become a Java Rockstar?
Subscribe to our newsletter to start Rocking right now!
To get you started we give you our best selling eBooks for FREE!
1. JPA Mini Book
2. JVM Troubleshooting Guide
3. JUnit Tutorial for Unit Testing
4. Java Annotations Tutorial
5. Java Interview Questions
6. Spring Interview Questions
7. Android UI Design
and many more ....
I agree to the Terms and Privacy Policy

Mary Zheng

Mary graduated from the Mechanical Engineering department at ShangHai JiaoTong University. She also holds a Master degree in Computer Science from Webster University. During her studies she has been involved with a large number of projects ranging from programming and software engineering. She worked as a lead Software Engineer where she led and worked with others to design, implement, and monitor the software solution.
Subscribe
Notify of
guest


This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button