Dissecting factory methods for collections – Arrays, collections and data structures

0 Comments 7:03 AM

109. Dissecting factory methods for collections

Factory methods for collections are a must-have skill. Is very convenient to be able to quickly and effortlessly create and populate unmodifiable/immutable collections before putting them to work.

Factory methods for maps

For instance, before JDK 9, creating an unmodifiable map could be accomplished like this:

Map<Integer, String> map = new HashMap<>();
map.put(1, “Java Coding Problems, First Edition”);
map.put(2, “The Complete Coding Interview Guide in Java”);
map.put(3, “jOOQ Masterclass”);
Map<Integer, String> imap = Collections.unmodifiableMap(map);

This is useful if, at some point in time, you need an unmodifiable map from a modifiable one. Otherwise, you can shortcut this a little bit as follows (this is known as the double-brace initialization technique and as an anti-pattern):

Map<Integer, String> imap = Collections.unmodifiableMap(
 new HashMap<Integer, String>() {
  {
    put(1, “Java Coding Problems, First Edition”);
    put(2, “The Complete Coding Interview Guide in Java”);
    put(3, “jOOQ Masterclass”);
  }
});

If you need to return an unmodifiable/immutable map from a Stream of java.util.Map.entry then here you go:

Map<Integer, String> imap = Stream.of(
  entry(1, “Java Coding Problems, First Edition”),
  entry(2, “The Complete Coding Interview Guide in Java”),
  entry(3, “jOOQ Masterclass”))
 .collect(collectingAndThen(
   toMap(e -> e.getKey(), e -> e.getValue()),
   Collections::unmodifiableMap));

Moreover, let’s not forget the empty and singleton maps (quite useful to return from a method a map instead of null):

Map<Integer, String> imap = Collections.emptyMap();
Map<Integer, String> imap = Collections.singletonMap(
  1, “Java Coding Problems, First Edition”);

Starting with JDK 9, we can rely on a more convenient approach for creating unmodifiable/immutable maps thanks to JEP 269: Convenience Factory Methods for Collections. This approach consists of Map.of() which is available from 0 to 10 mappings or, in other words, is overloaded to support 0 to 10 key-value pairs. Here, we use the Map.of() for three mappings:

Map<Integer, String> imap = Map.of(
  1, “Java Coding Problems, First Edition”,
  2, “The Complete Coding Interview Guide in Java”,
  3, “jOOQ Masterclass”
);

Maps created via Map.of() don’t allow null keys or values. Such attempts will end up in a NullPointerException.If you need more than 10 mappings then you can rely on static <K,V> Map<K,V> ofEntries(Entry<? extends K,? extends V>… entries) as follows:

import static java.util.Map.entry;

Map<Integer, String> imap2jdk9 = Map.ofEntries(
  entry(1, “Java Coding Problems, First Edition”),
  entry(2, “The Complete Coding Interview Guide in Java”),
  entry(3, “jOOQ Masterclass”)
);

Finally, creating an unmodifiable/immutable map from an existing one can be done via static <K,V> Map<K,V> copyOf(Map<? extends K,? extends V> map):

Map<Integer, String> imap = Map.copyOf(map);

If the given map is unmodifiable then most probably Java will not create a copy and will return an existing instance. In other words, imap == map will return true. If the given map is modifiable then most probably the factory will return a new instance, so imap==map will return false.

Factory methods for lists

Before JDK 9, a modifiable List can be used for creating an unmodifiable List with the same content as follows:

List<String> list = new ArrayList<>();
list.add(“Java Coding Problems, First Edition”);
list.add(“The Complete Coding Interview Guide in Java”);
list.add(“jOOQ Masterclass”);
List<String> ilist = Collections.unmodifiableList(list);

A common approach for creating a List consists of using Arrays.asList():

List<String> ilist = Arrays.asList(
  “Java Coding Problems, First Edition”,
  “The Complete Coding Interview Guide in Java”,
  “jOOQ Masterclass”
);

However, keep in mind that this is a fixed-size list not an unmodifiable/immutable list. In other words, operations that attempt to modify the list size (for instance, ilist.add(…)) will result in UnsupportedOperationException, while operations that modify the current content of the list (for instance, ilist.set(…)) are allowed.If you need to return an unmodifiable/immutable List from a Stream then here you go:

List<String> ilist = Stream.of(
  “Java Coding Problems, First Edition”,
  “The Complete Coding Interview Guide in Java”,
  “jOOQ Masterclass”)
  .collect(collectingAndThen(toList(),
           Collections::unmodifiableList));

Moreover, creating an empty/singleton list can be done as follows:

List<String> ilist = Collections.emptyList();
List<String> ilist = Collections.singletonList(
  “Java Coding Problems, First Edition”);

Starting with JDK 9+, is more convenient to rely on the factory methods List.of() available for 0 to 10 elements (null elements are not allowed):

List<String> ilist = List.of(
  “Java Coding Problems, First Edition”,
  “The Complete Coding Interview Guide in Java”,
  “jOOQ Masterclass”);

If we need a copy of an existing list then rely on List.copyOf():

List<String> ilist = List.copyOf(list);

If the given list is unmodifiable then most probably Java will not create a copy and will return an existing instance. In other words, ilist == list will return true. If the given list is modifiable then most probably the factory will return a new instance, so ilist == list will return false.

Factory methods for sets

Creating Sets follows the same path as Lists. However, pay attention that there is no singletonSet(). For creating a singleton set simply call singleton():

Set<String> iset = Collections.singleton(
  “Java Coding Problems, First Edition”);

You can find more examples in the bundled code. You may also be interested in Problem x from Java Coding Problems, First Edition, which covers the unmodifiable versus immutable collections. Moreover, please consider the following problem as well, since it brings more info in this context.

Leave a Reply

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

Introducing the canonical and compact constructors for records 2 – Record and record patternIntroducing the canonical and compact constructors for records 2 – Record and record pattern

Reassigning components Via an explicit canonical/compact constructor we can reassign components. For instance, when we create a MelonRecord we provide its type (for instance, Cantaloupe) and its weight in grams

Adding more artifacts in a record Certification Exams of Java Getting a list from a stream Java Exams Tackling guarded record patterns Tackling records in Spring Boot Understanding records serialization

Understanding records serialization 2 – Record and record patternUnderstanding records serialization 2 – Record and record pattern

Serializing/deserialzing gacContainer (typical Java class) The gacContainer object is an instance of MelonContainer which is a plain Java class. MelonContainer gacContainer = new MelonContainer(  LocalDate.now().plusDays(15), “ML9000SQA0”,    new Melon(“Gac”, 5000)); After

Adding more artifacts in a record Certification Exams of Java Getting a list from a stream Java Exams