Open In App

Java Convenience Factory Methods for Collections

Improve
Improve
Like Article
Like
Save
Share
Report

The JDK 9 has added static factory methods like of() onto basic Collection interfaces, to create unmodifiable collection objects. These are the same as the unmodifiable (immutable) collection we create in JDK 6, 7, 8. It allows you to create a list, set, and map of values in just one line. Java 9 doesn’t bring as many dramatic changes to our way of coding as its predecessor did, but surely we will have some fancy features.

 The belowmentioned ways lead to the creation of unnecessary objects. To overcome this problem, Java 9 has introduced static factory methods to create an immutable list, set, and map which will be discussed later on. prior to it let us revise the creation of an unmodifiable Collection prior to JDK 9 (JDK 7, 8).

List in JDK 8

List<String> listOfString = new ArrayList<>();
listOfString.add("Geeks");
listOfString.add("Java");
listOfString.add("Kotlin");
listOfString.add("Groovy");
listOfString.add("Scala");
listOfString = Collections.unmodifiableList(listOfString);

 Way 1: Using double-braces initialization

List<String> listOfString = Collections.unmodifiableList(new ArrayList<>() {

    {

        add(“Geeks”);

        add(“Java”);

        add(“Kotlin”);

        add(“Groovy”);

        add(“Scala”);

    }

});

Way 2: Using the Arrays API to convert an array to an ArrayList

List<String> listOfString = Collections.unmodifiableList(Arrays.asList(“Geeks”,”Java”,”Kotlin”,”Groovy”, “Scala”));

Way 3: Using the Stream API 

List<String> listOfString = Collections.unmodifiableList(Stream.of(“Bruce”,”Steve”,”Adrian”, “Dave”, “Janick”,”Nicko”).collect(toList()));

Why Java 9?

Now let us see benefits prior why to use Java 9 static factory methods before jumping on them

  • Allows to create a list, set, and map of values in just one line.
  • Creating immutable objects using static factory methods prevents us from inserting nulls or duplicates (in Sets or Maps).
  • We are safe from getting NullPointerExceptions while traversing collection elements.

Now let us discuss the creation of an unmodifiable Collection in JDK. Here list, Set, and map are discussed below.

1. List in Java 9 

List<String> listOfString = List.of("Geeks", "Java", "Kotlin", "Groovy", "Scala");

Just one line of code is enough to create an Unmodifiable List, hence the creation of unnecessary objects is not involved.

  • List.of() allows you to create an empty immutable list.
  • Any modification will cause UnsupportedOperationException.
  • Several overloaded versions of List.of() are available from the performance perspective.

An immutable list has an abstract base class  AbstractImmutableList<E> and four implementations:

  • List0<E>
  • List1<E>
  • List2<E>
  • ListN<E>

Each of these types corresponds to the number of elements that are used to their creation. In the java.util.List interface, we have 12 static factory methods that use the above implementations to create immutable objects :

// creates empty immutable list
static <E> List<E> of()

// creates one-element immutable list
static <E> List<E> of(E e1)

// creates two-element immutable list
static <E> List<E> of(E e1, E e2)

// creates ten-element immutable list
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)

// creates N-element immutable list
static <E> List<E> of(E... elements)

Also do sneak out the methods that throw UnsupportedOperationException namely as follows

  • add(E e), addAll(Collection<? extends E> c), addAll(int index, Collection<? extends E> c)
  • remove(Object o), removeAll(Collection<?> c), removeIf(Predicate<? super E> filter)
  • replaceAll(UnaryOperator<E> operator)
  • retainAll(Collection<?> c)
  • sort(Comparator<? super E> c)
  • clear()

Inserting Null values will cause NullPointerException  as shown below

List<String> listOfString = List.of("Geeks","Java","Kotlin", "Scala", "Groovy", null);
// throws NullPointerException

Hence, the valid creation of an immutable list is as follows: 

List<String> listOfString = List.of("Geeks","Java","Kotlin", "Scala", "Groovy","Pearl");

2. Set in Java 9 

  • Set.of() allows you to create an empty immutable set.
  • Any modification will cause UnsupportedOperationException.
  • Several overloaded versions of Set.of() are available from the performance point of view.

Illustration:

An immutable set is implemented similarly to how we saw with the List interface. It has an abstract base class  AbstractImmutableSet<E> and four implementations:

  • Set0<E>
  • Set1<E>
  • Set2<E>
  • SetN<E>

That again corresponds to the number of elements that are used in their creation. In the  java.util.Set interface, we have 12 static factory methods: 

// creates empty immutable set
static <E> Set<E> of()

// creates one-element immutable set
static <E> Set<E> of(E e1)

// creates two-element immutable set
static <E> Set<E> of(E e1, E e2)

// creates ten-element immutable set
static <E> Set<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10)

// creates N-element immutable set
static <E> Set<E> of(E... elements)

Methods that throw UnsupportedOperationException are as follows:

  1. add(E e), addAll(Collection<? extends E> c)
  2. remove(Object o), removeAll(Collection<?> c), removeIf(Predicate<? super E> filter)
  3. retainAll(Collection<?> c)
  4. clear()

Illustration:

Like with immutable lists, we cannot instantiate a Set with a null value as it does throw out NullPointerException where adding duplicate will lead to IllegalArgumentException as shown below:

Set<String> setOfString = Set.of("Geeks", "Java", "Kotlin", "Scala", "Groovy", null);
// throws NullPointerException

Set<String> setOfString = Set.of("Geeks", "Java", "Kotlin", "Java");
// throws IllegalArgumentException

Hence, the valid creation of an immutable set is as follows:

Set<String> setOfString = Set.of("Geeks", "Java", "Kotlin", "Scala", "Groovy", "Pearl");

3. Map in Java 9 

  • Map.of() and Map.ofEntries() allow you to create an immutable map.
  • Any modification will cause UnsupportedOperationException.
  • Map.of() is overloaded to create a map of at most 10 key-value pairs.
  • Map.ofEntries() will be used to create a map of more than 10 key-value pairs.

An immutable map has an abstract base class,  AbstractImmutableMap<K, V>, with three implementations:

  • Map0<K, V>
  • Map1<K, V>
  • MapN<K, V>

Again we have the following set of factory methods inside the java.util.Map interface. 

// creates an empty map
static <K, V> Map<K, V> of()

// creates one-element map
static <K, V> Map<K, V> of(K k1, V v1)

// creates two-element map
static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2)

// creates ten-element map
static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4,
                           K k5, V v5, K k6, V v6, K k7, V v7, K k8, V v8, 
                           K k9, V v9, K k10, V v10)

// creates N-element map
static <K, V> Map<K, V> ofEntries(Entry<? extends K, ? extends V>... entries)

Again the methods that throw an UnsupportedOperationException are as follows:

  • clear()
  • compute(K key, BiFunction<? super K,? super V,? extends V> rf)
  • computeIfAbsent(K key, Function<? super K,? extends V> mf)
  • computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> rf)
  • merge(K key, V value, BiFunction<? super V,? super V,? extends V> rf)
  • put(K key, V value), putAll(Map<? extends K,? extends V> m), putIfAbsent(K key, V value)
  • remove(Object key), remove(Object key, Object value)
  • replace(K key, V value), replace(K key, V oldValue, V newValue), replaceAll(BiFunction<? super K,? super V,? extends V> f)

Illustration:

Inserting Null key or value will cause NullPointerException : 

Map<String, Integer> weightInKg = Map.of(null, 59, "John", 61);
// throws NullPointerExcepton because of null key
Map<String, Integer> weightInKg = Map.of("Ron", null, "John", 61);
// throws NullPointerExcepton because of null value
Map<String, Integer> weightInKg = Map.ofEntries(Map.entry("Ron", 59), null);
// throws NullPointerExcepton because of null entry

Adding duplicate key element will throw IllegalArgumentException:

Map<String, Integer> weightInKg = Map.of("Ron", 59, "Ron", 59);
Map<String, Long> weightInKg = Map.ofEntries(Map.entry("Ron", 59), Map.entry("Ron", 59));

Valid creation of an immutable map is as follows:

Map<String, Long> weightInKg = Map.of("Ron", 59, "John", 61, "Ed", 60, "Nick", 60, "Jack", 60L, "Ben", 65);
Map<String, Long> age = Map.ofEntries(Map.entry("Ron", 59),
                                      Map.entry("John", 61),
                                      Map.entry("Ed", 60),
                                      Map.entry("Nick", 60),
                                      Map.entry("Jack", 60),
                                      Map.entry("Ben", 65));


Last Updated : 03 Mar, 2021
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads