Multimaps - Google Guava
Join the DZone community and get the full member experience.
Join For FreeThis 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, and function and predicate
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.
So, that's all for now. In my next post, I'll be introducing the BiMap
Â
Published at DZone with permission of Tom Jefferys, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments