Creating Unmodifiable Lists – Collections, Part I: ArrayList

Creating Unmodifiable Lists

Unmodifiable collections are useful to prevent a collection from accidently being modified, as doing so might cause the program to behave incorrectly. Such collections are also stored efficiently, as no bookkeeping is required to support any further modifications and data in the collection can be packed more densely since it can never change.

Here we look at how to create unmodifiable lists. Later we will discuss unmodifiable sets (§15.4, p. 804) and unmodifiable maps (§15.8, p. 832), and we will also contrast unmodifiable collections with unmodifiable views of collections (§15.11, p. 856).

The List<E> interface provides generic static methods to create unmodifiable lists that have the following characteristics:

  • An unmodifiable list cannot be modified structurally; for example, elements cannot be added, removed, replaced, or sorted in such a list. Any such attempt will result in an UnsupportedOperationException to be thrown. However, if the elements themselves are mutable, the elements may appear modified.
  • Although duplicates are allowed, unmodifiable lists do not allow null elements, and will result in a NullPointerException if an attempt is made to create them with the null elements.
  • The order of the elements in an unmodifiable list is the same as the order of the arguments or the order of the elements in the array of the variable arity argument of the static method.
  • An unmodifiable list can be serialized if its elements are serializable (§20.5, p. 1261).

Click here to view code image

static <E> List<E> of(E e1, E e2, E e3, E e4, E e5,
                      E e6, E e7, E e8, E e9, E e10)

The of() method is overloaded. The 11 overloaded methods are fixed-argument methods for accepting 0 to 10 arguments. They return an unmodifiable list containing the number of elements specified. They throw a NullPointerException, if an element is null. These overloaded methods are convenient for creating short lists.

Click here to view code image

@SafeVarargs static <E> List<E> of(E… elements)

This variable arity method returns an unmodifiable list containing an arbitrary number of elements specified by its variable arity argument. It throws a Null-PointerException, if an element is null or if the array of the variable arity parameter is null.

The @SafeVarargs annotation suppresses the heap pollution warning in the method declaration and also the unchecked generic array creation warning at the call sites (§25.5, p. 1585).

Click here to view code image

static <E> List<E> copyOf(Collection<? extends E> collection)

This generic method returns an unmodifiable list containing the elements of the specified collection, in its iteration order. The specified collection must not be null, and it must not contain any null elements—otherwise, a NullPointerException is thrown. If the specified collection is subsequently modified, the returned list will not reflect such modifications.

The code below shows that a list created by the List.of() method cannot be modified. The list returned is also not an instance of ArrayList.

Click here to view code image

List<String> list = List.of(“Tom”, “Dick”, “Harriet”);
//  list.add(“Harry”);                          // UnsupportedOperationException
//  list.remove(2);                             // UnsupportedOperationException
//  list.set(0, “Tommy”);                       // UnsupportedOperationException
System.out.println(list);                       // [Tom, Dick, Harriet]
System.out.println(list instanceof ArrayList);  // false

The List.of() method does not allow null elements:

Click here to view code image

List<String> coinList = List.of(“nickel”, “dime”, null); // NullPointerException

For arguments up to 10, an appropriate fixed-arity List.of() method is called. For more than 10 arguments, the variable arity List.of(E…) method is called, passing an implicitly created array that contains the arguments.

Click here to view code image

List<Integer> intList1 = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);   // Fixed-arity
List<Integer> intList2 = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11); // Varargs
System.out.println(intList1);        // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
System.out.println(intList2);        // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

At (1) below, an explicit array is passed as an argument, resulting in the variable arity List.of(E…) method being called, creating a list of String. At (2), the method call explicitly specifies the type of its argument as String[]. In this case the one-argument List.of(E) method is called, creating a list of length 1 and whose element type is String[].

Click here to view code image

String[] strArray = {“Tom”, “Dick”, “Harriet”};
List<String> strList = List.of(strArray);                  // (1) List of String
List<String[]> strArrayList = List.<String[]>of(strArray); // (2) List of String[]
System.out.println(strList);       // [Tom, Dick, Harriet]
System.out.println(strArrayList);  // [[Ljava.lang.String;@3b22cdd0]

The code below shows how we can make a copy of a collection, in this case, a list. The copyOf() method creates a copy of the list passed as an argument at (1). The list created is unmodifiable analogous to the lists created with the List.of() methods. The code also shows that modifying the original list does not reflect in the copy of the list.

Click here to view code image

List<String> fab4 = new ArrayList<>();
fab4.add(“John”); fab4.add(“Paul”); fab4.add(“George”); fab4.add(“Ringo”);
System.out.println(fab4);                    // [John, Paul, George, Ringo]
List<String> fabAlways = List.copyOf(fab4);  // (1)
fab4.remove(“John”); fab4.remove(“George”);  // Modify original list
System.out.println(fab4);                    // [Paul, Ringo]
System.out.println(fabAlways);               // [John, Paul, George, Ringo]

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *