What is a Java multimap?
One of the strengths of the Java programming language is that it includes built-in implementations of common data structures, such as maps, lists, sets, and queues. However, third-party Java libraries also include additional data structures, such as multimaps, for users who need more complex functionality. But what is a Java multimap exactly, and how do Java multimaps work?
What are Java multimaps?
The Java map is a collection that stores key-value pairs: a single unique key is associated with a single value. Java multimaps take this concept and extend it so that it can apply to additional use cases.
Java multimaps are a data structure first introduced in Guava, a set of Java core libraries developed by Google. As the name suggests, a multimap allows you to map a single key to one or more values—unlike traditional maps, in which each key may only be mapped to a single value.
Why are Java multimaps useful? Multimaps can help prevent your code from breaking if it’s possible (but unlikely) that multiple values share the same key.
For example, hash tables are a data structure that convert an object into an alphanumeric hash based on a mathematically complicated hash function, and then associate that hash key with the original object. The hash function is designed to generate unique hashes for each object, if possible.
However, in very rare cases, there may be a collision between two different objects that have the same hash value. Using a Java multimap, instead of a plain map, lets you accommodate these collisions by associating both objects with the same hash key.
How do Java multimaps work?
Java multimaps are implemented by the com.google.common.collect.Multimap interface. The most important methods of Java multimaps include:
- clear(): Removes all elements from the multimap.
- containsKey(): Returns a Boolean value based on whether the multimap contains the given key.
- containsValue(): Returns a Boolean value based on whether the multimap contains the given value.
- get(): Given a key in the multimap, returns a collection of the associated value(s).
- isEmpty(): Returns a Boolean value based on whether the multimap is empty.
- put(): Places the given key in the multimap and associates it with the given value.
- remove(): Removes the given key-value pair from the multimap.
- size(): Returns the number of key-value pairs in the multimap.
Because com.google.common.collect.Multimap is only an interface, it cannot be instantiated directly in Java. Instead, users need to instantiate one of the Guava classes that implements the Multimap interface, such as:
- ArrayListMultimap: The ArrayListMultimap uses Java's ArrayLists to store keys and values. If a key is associated with multiple values, the values are stored in the order that they were inserted into the multimap.
- HashMultimap: The HashMultimap implements multimaps using a hash table.
- LinkedListMultimap: The LinkedListMultimap uses linked lists to store keys and values. The items in the multimap are stored deterministically, which means that you can easily iterate through them.
- TreeMultimap: The TreeMultimap uses a tree to store keys and values in their natural ordering. For example, if the keys in the multimap are integers, the nodes in the left subtree will have integer keys that are less than the current key, and the nodes in the right subtree will have integer keys that are greater than the current key.
Java multimaps and Redis
Redis is an open-source, in-memory data structure store used to build NoSQL key-value databases, caches, and message brokers. While Redis includes several fundamental data types such as lists, sets, and hashes, it does not include an implementation of multimaps. Redis is also not compatible with programming languages such as Java out of the box, which can make the learning curve more difficult for Java developers.
For this reason, many Java developers choose to install a third-party Redis Java client such as Redisson. Redisson includes many familiar Java objects, collections, and constructs—including multimaps using the RMultimap interface. Below is a simple example that demonstrates the use of the RMultimap interface in Redisson:
RSetMultimap<String, String> setMultimap = redisson.getSetMultimap("myFish"); // Add items setMultimap.put("favoriteFish", "Flagfin"); setMultimap.put("favoriteFish", "Shiner"); setMultimap.put("favoriteFish", "Ladyfish"); setMultimap.put("oceanFish", "Shark"); setMultimap.put("oceanFish", "Ocean sunfish"); // Remove items setMultimap.remove("oceanFish", "Shark"); setMultimap.remove("favoriteFish", "Flagfin"); // Get all items Set<String> favoriteFish = setMultimap.get("favoriteFish"); // Get number of key-value pairs setMultimap.size(); // Get number of entries per key setMultimap.get("favoriteFish").size(); // Check for key-value pair setMultimap.containsEntry("favoriteFish", "Ladyfish");