Collections in Java
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 implementationTreeSet
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)
Related materials
Java official
- (Java 11): Collections Framework Overview
- Java Platform, Standard Edition Core Libraries: Creating Immutable Lists, Sets, and Maps
Guava official
- ImmutableCollectionsExplained · google/guava Wiki · GitHub
- UsingAndAvoidingNullExplained · google/guava Wiki · GitHub