The arrival of Java 9 brings many new features to Java's Collections API, one of which being collection factory methods.
What are they and how can I implement them properly?
The arrival of Java 9 brings many new features to Java's Collections API, one of which being collection factory methods.
What are they and how can I implement them properly?
Note 1: To prevent the use of raw-types, I have opted to provide a generic type for each class that I mention below by using
E
, representing an element of aCollection<E>
.Note 2: This answer is subject to change; please edit this post if a typo has occurred.
What are collection factory methods?
A collection factory method in Java is a static method that provides a simple way of initializing an immutable
Collection<E>
.Being immutable, no elements can be added to, removed from, or modified inside the
Collection<E>
after it is initialized.With Java 9, collection factory methods are provided for the following interfaces:
List<E>
,Set<E>
, andMap<K, V>
What do they improve?
Up until Java 9, there has been no simple, universal method to initialize a
Collection<E>
with initial elements/key-value entries. Previously, developers were required to initialize them as follows (assuming the generic typesE
,K
, andV
have been replaced withInteger
):List<Integer>
List<Integer>
with initial elements, however the result is simply a view of aList<Integer>
; we are unable to add to or remove from thisList<Integer>
, but we are still able to modify existing elements by usingList#set
.List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
List<Integer>
to be entirely mutable, then we would have to pass it to the constructor of anArrayList<Integer>
, for example:List<Integer> mutableList = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
Set<Integer>
Set<Integer>
required more code to initialize with initial elements than aList<Integer>
does (seeing as aList<Integer>
is required to initialize aSet<Integer>
with initial elements), which can be seen below.Set<Integer> mutableSet = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5));
Map<Integer, Integer>
Map<Integer, Integer>
is arguably the most complicated to initialize with initial key-value entries; however, there are multiple ways to go about it.Map<Integer, Integer>
and simply callMap#put
to add key-value entries.Map#put
to be called.Why should I use them?
I argue that collection factory methods provide the developer with a concise method of initializing a
List<E>
,Set<E>
, orMap<K, V>
with initial elements/key-value entries, which can be seen by the examples below.What is the proper syntax to use?
For simplicity, these examples will replace the generic types
E
,K
, andV
withInteger
.List<Integer>
List<Integer> list = List.of();
List<Integer>
.List<Integer> list = List.of(1);
List<Integer>
with one element.List<Integer> list = List.of(1, 2);
List<Integer>
with two elements.List<Integer> list = List.of(1, 2, 3, 4, 5, ...);
List<Integer>
with a variable amount of elements.Set<Integer>
Set<Integer> set = Set.of();
Set<Integer>
.Set<Integer> set = Set.of(1);
Set<Integer>
with one element.Set<Integer> set = Set.of(1, 2);
Set<Integer>
with two elements.Set<Integer> set = Set.of(1, 2, 3, 4, 5, ...);
Set<Integer>
with a variable amount of elements.Map<Integer, Integer>
Map<Integer, Integer> map = Map.of();
Map<Integer, Integer>
.Map<Integer, Integer> map = Map.of(1, 2);
Map<Integer, Integer>
with one key-value entry.1
and the value is2
.Map<Integer, Integer> map = Map.of(1, 2, 3, 4);
Map<Integer, Integer>
with two key-value entries.1
and3
and the values are2
and4
.Map<Integer, Integer> map = Map.ofEntries(Map.entry(1, 2), Map.entry(3, 4), ...);
Map<Integer, Integer>
with a variable amount of key-value entries.As you can see, this new method of initialization requires less code than its predecessors.
Can I use collection factory methods to create mutable objects?
The
Collection<E>
created by collection factory methods are inherently immutable, however we are able to pass them to a constructor of an implementation of theCollection<E>
to produce a mutable version:List<Integer>
List<Integer> mutableList = new ArrayList<>(List.of(1, 2, 3, 4, 5));
Set<Integer>
Set<Integer> mutableSet = new HashSet<>(Set.of(1, 2, 3, 4, 5));
Map<Integer, Integer>
Map<Integer, Integer> mutableMap = new HashMap<>(Map.of(1, 2, 3, 4));