Google Guava MultiMaps
Guava?
This is the first in a series of posts where I’ll be attempting to explain and explore Google’s awesome Guava java library.
I first came across Guava whilst searching for generic versions of Apache Commons Collections – I needed a Bimap
and was fed up with having to pepper my code with casts – however what I found was much much better.
Not only does it contain various implementations of more complex (but useful) collection types-Multimaps,Multisets,Bimaps– which I’ll discuss in detail, but also facilities to support a more functional style of programming with immutable collections, andfunction andpredicate objects. This has both completely changed the way I write java, and at the same time made me increasingly frustrated with Java’s sometimes clunky syntax, something I intend to explore in further posts.
Anyway enough with the introduction, and on with the good stuff. The first thing I’d like to take a look at is the Multimap, which is probably the single Guava feature I’ve made the most use of.
Mutlimaps
So, how often have you needed a data structure like the following?
Map<String,List<MyClass>> myClassListMap test2 = new HashMap<String,List<MyClass>>()
If you’re anything like me, fairly frequently. And don’t you find yourself writing the same boilerplate code over and over again?
To put a key/value pair into this map, you need to first check if a list already exists for your key, and if it doesn’t create it. You’ll end up writing something along the lines of the following:
void putMyObject(String key, Object value) { List<Object> myClassList = myClassListMap.get(key); if(myClassList == null) { myClassList = new ArrayList<object>(); myClassListMap.put(key,myClassList); } myClassList.add(value); }
Bit of a pain, and what if you need methods to check a value exists, or remove a value, or even iterate over the entire data structure. That can be quite a lot of code.
Never fear Guava is here!
Just like the standard java collections, Guava defines several interfaces and matching implementations. Usually you want to code to an interface, and only worry about the implementation when you create it. In this case we’re interested in Multimaps.
So using a multimap, we could replace the data structure declaration with the following:
Multimap<String,Object> myMultimap = ArrayListMultimap.create();
There’s a few things to note here. The generic type declaration should look very familiar, this is exactly how you would declare a normal Map.
You may have been expecting to see new ArrayListMultimap<String,Object>()
on the right-hand side of the equals. Well, all Guava collection implementations offer a create
method, which is usually more concise and has the advantage that you do not have to duplicate the generic type information.
Guava in fact adds similar functionality to the standard Java collections. For example, if you examine com.google.common.collect.Lists
, you’ll see static newArrayList()
, and newLinkedList()
methods, so you can take advantage of this conciseness even with the standard Java collections. (I’ll aim to cover this in more detail in a future post).
So we’ve declared and instantiated a multimap, how do we go about using them? Easy just like a normal map!
public class MutliMapTest { public static void main(String... args) { Multimap<String, String> myMultimap = ArrayListMultimap.create(); // Adding some key/value myMultimap.put('Fruits', 'Bannana'); myMultimap.put('Fruits', 'Apple'); myMultimap.put('Fruits', 'Pear'); myMultimap.put('Vegetables', 'Carrot'); // Getting the size int size = myMultimap.size(); System.out.println(size); // 4 // Getting values Collection<string> fruits = myMultimap.get('Fruits'); System.out.println(fruits); // [Bannana, Apple, Pear] Collection<string> vegetables = myMultimap.get('Vegetables'); System.out.println(vegetables); // [Carrot] // Iterating over entire Mutlimap for(String value : myMultimap.values()) { System.out.println(value); } // Removing a single value myMultimap.remove('Fruits','Pear'); System.out.println(myMultimap.get('Fruits')); // [Bannana, Pear] // Remove all values for a key myMultimap.removeAll('Fruits'); System.out.println(myMultimap.get('Fruits')); // [] (Empty Collection!) } }
One thing you may be wondering, is why does the get method return a Collection
and not a List
, that would be much more useful. Indeed it would. The problem is there are several different implementations available, some use Lists-ArrayListMultimap
, LinkedListMultimap
etc. – and some use Sets – HashMultimap
,TreeMultimap
among others.
To handle this – if you need to work directly with the Lists, or Sets in the map – there are several subinterfaces defined. ListMultimap
, SetMultimap
, and SortedSetMultimap
. These all do what you’d expect, and their methods that return collections, will return one of the approprite type.
ie
ListMutlimap<String,String> myMutlimap = ArrayListMultimap.create(); List<string> myValues = myMutlimap.get('myKey'); // Returns a List, not a Collection.
That’s basically all there is to them. I recommend looking at the API:http://docs.guava-libraries.googlecode.com/git-history/release09/javadoc/com/google/common/collect/Multimap.html, where you can find the various implementations, you should be able to find one that suits your needs.
Reference: Multimaps – Google Guava from our JCG partner Tom Jefferys at the Tom’s Programming Blog blog.
27 // Removing a single value
28 myMultimap.remove(‘Fruits’,’Pear’);
29 System.out.println(myMultimap.get(‘Fruits’)); // [Bannana, Pear]
probably [Bannana, Apple] ?
Hello.. nice article!
But in the MultiMapTest class… in the line 29, shouldn’t the result be [Banana, Apple] instead of [Banana, Pear], considering taht you removed [Pear] in the line 28?
Thanks! Keep up the good work!