1. Overview
In this quick tutorial, weβll have a look at the MultiValuedMap interface provided in the Apache Commons Collections library.
MultiValuedMap provides a simple API for mapping each key to a collection of values in Java. Itβs the successor to org.apache.commons.collections4.MultiMap, which was deprecated in Commons Collection 4.1.
2. Maven Dependency
For Maven projects, we need to add the commons-collections4 dependency:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.5.0-M2</version>
</dependency>
3. Adding Elements into a MultiValuedMap
We can add elements using the put and putAll methods.
Letβs start by creating an instance of MultiValuedMap:
MultiValuedMap<String, String> map = new ArrayListValuedHashMap<>();
Next, letβs see how we can add elements one at a time using the put method:
map.put("fruits", "apple");
map.put("fruits", "orange");
In addition, letβs add some elements using the putAll method, which maps a key to multiple elements in a single call:
map.putAll("vehicles", Arrays.asList("car", "bike"));
assertThat((Collection<String>) map.get("vehicles"))
.containsExactly("car", "bike");
4. Retrieving Elements from a MultiValuedMap
MultiValuedMap provides methods to retrieve keys, values, and key-value mappings. Letβs take a look at each of those.
4.1. Get All Values of a Key
To get all values associated with a key, we can use the get method, which returns a Collection:
assertThat((Collection<String>) map.get("fruits"))
.containsExactly("apple", "orange");
4.2. Get All Key-Value Mappings
Or, we can use the entries method to get a Collection of all key-value mappings contained in the map:
Collection<Map.Entry<String, String>> entries = map.entries();
4.3. Get All Keys
There are two methods for retrieving all the keys contained in a MultiValuedMap.
Letβs use the keys method to get a MultiSet view of the keys:
MultiSet<String> keys = map.keys();
assertThat(keys).contains("fruits", "vehicles");
Alternatively, we can get a Set view of the keys using the keySet method:
Set<String> keys = map.keySet();
assertThat(keys).contains("fruits", "vehicles");
4.4. Get All Values of a Map
Finally, if we want to get a Collection view of all values contained in the map, we can use the values method:
Collection<String> values = map.values();
assertThat(values).contains("apple", "orange", "car", "bike");
5. Removing Elements from a MultiValuedMap
Now, letβs look at all the methods for removing elements and key-value mappings.
5.1. Remove All Elements Mapped to a Key
First, letβs see how to remove all values associated with a specified key using the remove method:
Collection<String> removedValues = map.remove("fruits");
assertThat(map.containsKey("fruits")).isFalse();
assertThat(removedValues).contains("apple", "orange");
This method returns a Collection view of the removed values.
5.2. Remove a Single Key-Value Mapping
Now, suppose we have a key mapped to multiple values, but we want to remove only one of the mapped values, leaving the others. We can easily do this using the removeMapping method:
boolean isRemoved = map.removeMapping("fruits","apple");
assertThat(map.containsMapping("fruits","apple")).isFalse();
5.3. Remove All Key-Value Mappings
And finally, we can use the clear method to remove all mappings from the map:
map.clear();
assertThat(map.isEmpty()).isTrue();
6. Checking Elements from a MultiValuedMap
Next, letβs take a look at the various methods for checking whether a specified key or value exists in our map.
6.1. Check If a Key Exists
To find out whether our map contains a mapping for a specified key, we can use the containsKey method:
assertThat(map.containsKey("vehicles")).isTrue();
6.2. Check If a Value Exists
Next, suppose we want to check if at least one key in our map contains a mapping for a particular value. We can do this using the containsValue method:
assertThat(map.containsValue("orange")).isTrue();
6.3. Check If a Key-Value Mapping Exists
Similarly, if we want to check whether a map contains a mapping for a specific key and value pair, we can use the containsMapping method:
assertThat(map.containsMapping("fruits","orange")).isTrue();
6.4. Check If a Map Is Empty
To check if a map does not contain any key-value mappings at all, we can use the isEmpty method:
assertThat(map.isEmpty()).isFalse;
6.5. Check the Size of a Map
Finally, we can use the size method to get the total size of the map. When a map has keys with multiple values, then the total size of the map is the count of all the values from all keys:
assertEquals(4, map.size());
7. Implementations
The Apache Commons Collections Library also provides multiple implementations of this interface. Letβs have a look at them.
7.1. ArrayListValuedHashMap
An ArrayListValuedHashMap uses an ArrayList internally for storing the values associated with each key, so it allows duplicate key-values pairs:
MultiValuedMap<String, String> map = new ArrayListValuedHashMap<>();
map.put("fruits", "apple");
map.put("fruits", "orange");
map.put("fruits", "orange");
assertThat((Collection<String>) map.get("fruits"))
.containsExactly("apple", "orange", "orange");
Now, itβs worth noting that this class is not thread-safe. Therefore, if we want to use this map from multiple threads, we must be sure to use proper synchronization.
7.2. HashSetValuedHashMap
A HashSetValuedHashMap uses a HashSet for storing the values for each given key. Therefore, it doesnβt allow duplicate key-value pairs.
Letβs see a quick example, where we add the same key-value mapping twice:
MultiValuedMap<String, String> map = new HashSetValuedHashMap<>();
map.put("fruits", "apple");
map.put("fruits", "apple");
assertThat((Collection<String>) map.get("fruits"))
.containsExactly("apple");
Notice how, unlike our previous example that used ArrayListValuedHashMap, the HashSetValuedHashMap implementation ignores the duplicate mapping.
The HashSetValuedHashMap class is also not thread-safe.
7.3. UnmodifiableMultiValuedMap
The UnmodifiableMultiValuedMap is a decorator class that is useful when we need an immutable instance of a MultiValuedMap β that is, it shouldnβt allow further modifications:
@Test(expected = UnsupportedOperationException.class)
public void givenUnmodifiableMultiValuedMap_whenInserting_thenThrowingException() {
MultiValuedMap<String, String> map = new ArrayListValuedHashMap<>();
map.put("fruits", "apple");
map.put("fruits", "orange");
MultiValuedMap<String, String> immutableMap =
MultiMapUtils.unmodifiableMultiValuedMap(map);
immutableMap.put("fruits", "banana"); // throws exception
}
And again, itβs worth noting that modifying the final put will result in an UnsupportedOperationException.
8. Conclusion
Weβve seen various methods of the MultiValuedMap interface from the Apache Commons Collections library. In addition, weβve explored a few popular implementations.
