Tuesday, 25 October 2011

Collection creation and Immutability with Google Guava

So, thought I'd take a look at some of the collection creation patterns Guava offers, and also some of the Immutable collection types it offers.

If you've not seen my previous posts, you may want to start here:

Guava part 1 - MultiMaps
Guava part 2 - BiMaps
Guava part 3 - MultiSets

create methods

All of Guava's collection implementations contain one or more static create methods, these do what you'd expect, and generally offer a slightly more concise way of instantiating the collection classes.

Here are two different ways of creating a ArrayListMultimap

    Multimap<String,String> multimap1 = new ArrayListMultimap<String,String>();
    Multimap<String,String> multimap2 = ArrayListMultimap.create();

Ok, so there's not a huge amount in it - 12 characters in this example - but the way I see it, you're removing some redundancy, do we really need to reproduce the Generic type information twice?

That's nice, it's a shame Sun didn't think to add create methods to their Collection types when the created Java 5!

Again, Guava rides to your rescue here, and provides some utility classes for dealing with the standard collection types. com.google.common.collect.Lists, com.google.common.collect.Sets, and com.google.common.collect.Maps

These each provide several methods of the format newCollectionType(), here's some examples

    List<String> myList1 = new ArrayList<String>(); //old way
    List<String> myList2 = Lists.newArrayList();    //guava way

    Set<String> mySet1 = new HashSet<String>(); //old way
    Set<String> mySet2 = Sets.newHashSet();     //guava way

Since the "new" methods are static, you can cut out even more characters by using a static import ie...

    import static com.google.common.collect.Lists.newArrayList;
    import static com.google.common.collect.Sets.newHashSet;

    //elsewhere in code
    List<String> myList2 = newArrayList();
    Set<String> mySet2 = newHashSet();

Keypress wise you're not really saving a great deal using the guava methods, so I guess this is a matter of taste as much as anything else. Personally I think the Guava way reads much better, although I think I'd go without the static imports.

Good so far, but hardly earth shattering, what next?

Immutable collections

These are essentially collection objects that you can't change once they've been created, and are useful for all sorts of reasons. Guava provides Immutable implementations for most of the regular Collection interfaces: ImmutableList, ImmutableSet, and ImmutableMap , and also immutable implementations of some of the Guava collection interfaces ( ImmutableMultimap etc.)

My main use for them is creating static constants. For example lets say you need to hardcode a Set of Strings for some purpose. One way to do this might be, eg.

    private static final Set<String> farmAnimals =
                new HashSet<String>(Arrays.asList("Cow","Pig","Sheep"));

Doesn't look great does it, and it suffers from one major problem. Any code that can access this Set can also change it, which could lead to all sorts of unexpected problems.

Couldn't we just use Collection.unmodifiableSet(Set s) to solve this?

Well, in this particular example I guess we could write...

    private static final Set<String> farmAnimals =
         Collections.unmodifiableSet(
                new HashSet<String>(Arrays.asList("Cow","Pig","Sheep")));

...but that's starting to look a bit unwieldy, and the unmodifiable methods have one other problem. They only return an unmodifiable view of the collection, if you have a reference to the original collection, you can still alter it!

Whilst this may not be a problem in the last example, I still think a much better way of doing this is to use an ImmutableSet

    private static final Set<String> farmAnimals = 
                       ImmutableSet.of("Cow","Pig","Sheep");

That's much nicer isn't it! And there's several other ways we can create them, here's some examples:

    // use copyOf()...
    public void doStuffWithList(List<Object> unsafeList) {
       List<Object> safeList = ImmutableList.copyOf(unsafeList);
    }
    // use a builder...
    public Map<String,Integer> makeImmutableMap() {
        ImmutableMap.Builder<String,Integer> mapBuilder = 
                         new ImmutableMap.Builder<String,Integer>();
        Entry<String,Integer> entry = null;
        while((entry = getEntry()) != null) {
            mapBuilder.put(entry.getKey(), entry.getValue());
        }
        return builder.build();
    }
So, any other advantages of using Immutable collections?

Well there's several. They can simplify logic considerably, especially in multi-threaded environments. If threads only have read access to an object, them you don't need to worry about complicated thread synchronization logic

They are also slightly more efficient to use once they've been created. If a collection knows beforehand what it needs to store, and there's never going to be any changes, you can make various time and space savings. For example, most implementations of ArrayLists or HashMaps, will leave some unused space for new objects, so they don't have to constantly resize themselves. If you know there's never going to be any new objects, there's no need for this.

Finally you could also use them as hash keys. If the contents of a collection can't change, then neither will it's hashcode!

Any disadvantages?

There is of course one big disadvantage of Immutable objects, which is pretty obvious. You can't change them! If you need to alter the collection, you'll first need to take a copy of it. In certain situations - ie where concurrency is a concern - you may in fact want to take this approach. However this is going to be impractical where collections contain many many objects and you'll probably want a good old fashioned mutable collection (complete with synchronization code if required).

The only other thing to be aware of is, just because your collection is immutable, it doesn't mean the objects contained in them automatically are. If you can get a reference to an object in an immutable collection, then there's nothing to stop you changing any mutable state on that object! As a consequence it's best practice to make sure anything you keep in an immutable collection is immutable itself!