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.2</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.
And, as always, the full source code is available over on Github.