Overview
VAVR is a functional component library that provides persistent data types and functional control structures. I started using it recently, and really loved it! Its simplicity, immutable data types, and the functional programming concept are really remarkable. In this article, I will introduce VAVR’s HashMap, by doing a comparison with the built-in Java HashMap in Java 8.
After reading this article, you will understand:
- Map creation
- Map entries iteration
- Entries streaming
- Side effect
Map Creation
With built-in Java Collection API, you can create an empty HashMap
(java.util.HashMap
) and put entries into this map. The constructor does not
allow you to provide any entries:
// Java
Map<String, String> map = new HashMap<>();
map.put("cat", "🐱");
map.put("dog", "🐶");
With VAVR Collection API, you can create an empty HashMap
(io.vavr.collection.HashMap
), or create a HashMap with entries.
// VAVR
// Solution 1: of
Map<String, String> map = HashMap.of("cat", "🐱", "dog", "🐶");
// Solution 2: ofEntries
map = HashMap.ofEntries(Tuple.of("cat", "🐱"), Tuple.of("dog", "🐶"));
// Solution 3: ofAll
map = HashMap.ofAll(javaMap);
Map Entries Iteration
With built-in Java Collection API, you need to use method Map#entrySet()
to
retrieve the entry set from map. Then, use method Entry#getKey()
and
Entry#getValue
to retrieve the key and the value:
// Java
for (Map.Entry<String, String> e : map.entrySet()) {
System.out.println(e.getKey() + ": " + e.getValue());
}
// "cat: 🐱"
// "dog: 🐶"
You might ask, why HashMap does not implement Iterator
interface?
From dasblinkenlight’s answer on this Stack Overflow question, Map
in
general (and HashMap
in particular) do not implement Iterator
because it is
not clear what it should be iterating. There are three choices: Keys / Values /
Entries. None of the three choices above look entirely unreasonable: an argument
can be made in favor of each of these approaches. In the end, the library
designers decided not to make this choice for you, letting programmers pick what
to iterate explicitly.
With VAVR Collection API, they make the choice for you. Entries is the choice in
regards to iteration. Entries are represented as two-element tuple in VAVR
(java.util.function.Function.Tuple2
). Therefore, the iteration looks like:
// VAVR
for (Tuple2<String, String> t : map) {
System.out.println(t._1 + ": " + t._2);
}
// "cat: 🐱"
// "dog: 🐶"
Entries Streaming
In Java, if you want to map objects of type T to objects of type U, you can use streaming API. No matter what data types you choose, you need to start a stream, do the mapping, and finally collect the results. Let’s take an example of the previous map.
With built-in Java Collection API, you need to choose the entries, start a stream, apply the map, and collection the results. Although it is explicit, it is also very verbose:
// Java
List<String> list =
map.entrySet()
.stream()
.map(e -> e.getKey() + ": " + e.getValue())
.collect(Collectors.toList());
With VAVR Collection API, you can do the same in one line:
// VAVR
List<String> list = map.map(t -> t._1 + ": " + t._2).toList();
This is because VAVR chooses entry-iteration over key-iteration and
value-iteration. So starting a stream is easier. Then, in method Map#map()
,
the iterator is implicitly called behind the screen. Finally, the toList
collector is made as a shortcut for collecting values.
Side Effect
With built-in Java Collection API, when doing a Map#get()
, it
returns null
if this map contains no mapping for the key. This is dangerous
and requires extra caution about it. Either you need to check if the key exists,
or you need to check the null case on the returned value.
// Java
String cat = map.get("cat");
System.out.println(cat.isEmpty());
// false
String duck = map.get("duck");
System.out.println(duck.isEmpty());
// NullPointerException! 💥
With VAVR Collection API, when doing a Map#get()
, it returns an
Option<T>
, which is an equivalent type of java.util.Optional
. Therefore,
caller is informed about the situation, and understand that there’s no guarantee
to find a mapped key.
// VAVR
Option<String> cat = map.get("cat");
if (cat.isDefined()) {
...
}
Conclusion
In this article, we learnt a new data type Tuple
. We compared the usage of
VAVR’s HashMap and Java’s HashMap, in terms of creation, iteration, streaming,
and side effect. As usual, you can find the source code of this demo on GitHub:
mincong-h/java-examples -
VAVR.
Hope you enjoy this article, see you the next time!