TL;DR

  • As for collection usages in collaborative projects , I prefer to use Guava instead of Java API for consistency, simplicity, and safety.
  • Use immutable collections if we don’t need mutable ones to take advantages of performance and get rid of mis-operations around collections.
  • JDK’s Collections.unmodifiableXXX is an error-prone and need to understand the behavior deeply (not good for team-wise projects).

Motivation

  • Still not productive in Java due to my unfamiliarity.
  • Noticed that Guava is widely used as Java ecosystem; a set of core Java libraries from Google that includes new collection types (such as multimap and multiset), immutable collections.
  • Understand and compare on how to write collections in Java API and Guava.

Collections in Java

See https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/doc-files/coll-overview.html

  • Classes that implement the collection interfaces typically have names in the form of <Implementation-style><Interface>. The general purpose implementations are summarized in the following table:
Interface Hash Table Resizeble Array Balanced Tree Linked List Hash Table + Linked List
Set HashSet TreeSet LinkedHashSet
List ArrayList LinkedList
Deque ArrayDeque LinkedList
Map HashMap TreeMap LinkedHashMap
  • The following syntax represents a collection object construction.

    Interface<Type of elements> = new Implementation of the interface<>(ARGS);

As for the immutable collections in JDK, Oracle JDK 9 Documentation: Creating Immutable Lists, Sets, and Maps explains the details.

How to use Set

See The Java™ Tutorials: The Set Interface:

  • HashSet is a general purpose Map implementation
  • TreeSet stores its elements in a red-black tree, orders its elements based on their values; it is substantially slower than HashSet.
  • LinkedHashSet is implemented as a hash table with a linked list running through it, orders its elements based on the order in which they were inserted into the set (insertion-order).

This section focus on HashSet and immutable Set implementations in Java and Guava.

Instantiate empty HashSet

// Java
Set<String> mutableSet = new HashSet<>();

// (Don't use) Legacy syntax before Java 7
Set<String> mutableSet = new HashSet<String>();

// Guava
Set<String> mutableSet = Sets.newHashSet();

Instantiate a Set with elements

Mutable

// Java
Set<String> mutableSet = new HashSet<>(Arrays.asList("alice", "bob", "casey"));

or
        
Set<String> mutableSet = new HashSet<>(List.of("alice", "bob", "casey"));

// Guava
Set<String> mutableSet = Sets.newHashSet("alice", "bob", "casey");
Note

Although there are no advantages to use Guava to create an empty list but instantiation of the HashSet with Lists.newHashSet() is simpler than new HashSet<>(Arrays.asList()). So, if I’ve already used Guava somewhere in the project, I prefer to use Guava for HashSet. Using Guava just for a HashSet instantiation is an overkill and prefer to use Java’s standard new HashSet<>() in that case.

Immutable

// Java
Set<String> immutableSet = Set.of("java", "ruby", "go");

// Guava
Set<String> immutableSet = ImmutableSet.of("java", "ruby", "go");

How to use List

See The Java™ Tutorials: The List Interface and CollectionUtilitiesExplained · google/guava Wiki · GitHub

Instantiate an empty ArrayList

ArrayList is a general purpose List implementations.

// Java
List<String> list = new ArrayList<>();

// (Don't use) Legacy syntax before Java 7
List<String> list = new ArrayList<String>();

// Guava
List<String> list = Lists.newArrayList();

Instantiate List with elements

Mutable

// Java
List<String> list = new ArrayList<>(Arrays.asList("alpha","beta","gamma"));

// Guava
List<String> list = Lists.newArrayList("alpha","beta","gamma");
Note

Same as HashSet. Basically prefer to use Guava for the simplicity.

Immutable

// Java
List<String> immutableList = List.of("alpha","beta","gamma");

// Guava
List<String> immutableList = ImmutableList.of("alpha","beta","gamma");

(WIP) How to use Deque

To be added.

How to use Map

See https://docs.oracle.com/javase/tutorial/collections/interfaces/map.html.

This article takes a look at the Guava’s ImmutableMap but does not treat other Guava’s map implementations such as SortedMap, BiMap, Multimap, and RangeMap (https://www.baeldung.com/guava-maps and https://www.baeldung.com/guava-rangemap).

Instantiate an empty Map

Mutable

// Java
Map<String, String> mutableMap = new HashMap<>();

// (Don't use) Legacy syntax before Java 7
// mutable empty map
Map<String, String> mutableMap = new HashMap<String,String>();

// Guava
Map<String, String> mutableMap = Maps.newHashMap()

Instantiate Map with elements

Mutable

// Java (not recommended to create an anonymous subclass to avoid unexpected behavior)
// mutable map
Map<String, String> mutableMap = new HashMap<>() {
  {
    put("key1", "value1");
    put("key2", "value2");
  }
};

Immutable

// Java (this works for up to 10 elements)
Map<String, String> immutableMap = Map.of(
  "key1", "value1",
  "key2", "value2"
);

// Java (this works for any number of elements)
import static java.util.Map.entry;    
Map<String, String> immutableMap = Map.ofEntries(
  entry("key1", "value1"),
  entry("key2", "value2"),
);

// Guava (this works for any number of elements
// NOTE: new ImmutableMap.Builder<>() does not work
Map<String, String> immutableMap = new ImmutableMap.Builder<String, String>()
        .put("key1", "value1")
        .put("key2", "value2")
        .build();

// Guava (this works for any number of elements
Map<String, String> immutableMap = ImmutableMap.<String, String> builder()
     .put("key1", "value1")
     .put("key2", "value2")
     .build();
        
// Guava (this works for up to 5 elements.
// For small immutable maps, the ImmutableMap.of() methods are even more convenient.
Map<String, String> immutableMap = ImmutableMap.of(
  "key1", "value1",
  "key2", "value2"
);

Misc

Collections.unmodifiableXXX methods are error prone

The JDK provides Collections.unmodifiableXXX methods.

List<String> mutableList = new ArrayList<>(List.of("ruby", "go", "java"));
mutableList.add("python");

List<String> immutableList = Collections.unmodifiableList(mutableList);
//immutableList ==> [ruby, go, java, python]

immutableList.add("rust");
//|  Exception java.lang.UnsupportedOperationException
//|        at Collections$UnmodifiableCollection.add (Collections.java:1060)
//|        at (#8:1)

mutableList.add("rust");
mutableList
//mutableList ==> [ruby, go, java, python, rust]

immutableList
//immutableList ==> [ruby, go, java, python, rust]

Can people always think of the above Collections.unmodifiableXXX methods behavior? I don’t think so. Guava‘ s ImmutableXXX.copyOf() provides a safe guard to create a new immutable collections from mutable collections.

List<String> mutableList = new ArrayList<>(List.of("ruby", "go", "java"));
mutableList.add("python"); 
        
List<String> immutableList = ImmutableList.copyOf(mutableList);
// immutableList ==> [ruby, go, java, python]

immutableList.add("rust");
// Exception java.lang.UnsupportedOperationException
//  at ImmutableCollection.add (ImmutableCollection.java:264)
//  at (#16:1)

mutableList.add("rust");
mutableList
// mutableList ==> [ruby, go, java, python, rust]

immutableList
// immutableList ==> [ruby, go, java, python]

This is pretty important to work as teams. There are various reasons that all people in teams cannot understand the Java’ s Collections.unmodifiableXXX methods deeply (e.g. unfamilarity with Java, pushing PRs outside of projects). Even people understand the specifications, we overlook unexpected usages in the code. Also, ImmutableXXX.copyOf(ImmutableCollection) is not a simple copy method but tries to avoid a linear-time copy and it’s efficient than we think. See ImmutableCollectionsExplained · google/guava Wiki · GitHub .

Arrays.asList() vs List.of()

// Mutable list
List<String> list = Arrays.asList("1","2");
// OK
list.set(1, "new");
list ==> [1, new]

// Immutable List
List<String> list =List.of("1","2");
// NG
> list.set(1, "new")
Exception java.lang.UnsupportedOperationException
      at ImmutableCollections.uoe (ImmutableCollections.java:72)
      at ImmutableCollections$AbstractImmutableList.set (ImmutableCollections.java:110)
      at (#19:1)

Java official

Guava official

Java API vs Guava

Misc