Grrrrr…oovy – Collection Groans

September 1, 2009

I’m still learning Groovy and I generally like it but there are some bizarre irritations, probably because of my lack of understanding but also maybe just because…, well, I’ll let you judge for yourself in this example.

Groovy is very strong in providing additional methods for Java collections to make them more Ruby-like. A lot of the functions are also available via Apache Commons Collections or Google Collections but as they’re part of the language and so standardised they don’t require additional libraries.

Let’s look at three of those methods:

findIndexValues( Closure closure ) – when closure is true then the element has its index value returned. The closure is executed for each element and the results collected in another collection.

def list = [ "a", null, "b", "c" ]
def idxs = list.findIndexValues { it != null }
===> [ 0, 2, 3 ]

Pretty slick.

eachWithIndex( Closure closure ) – for each element in the collection, the closure is called with two parameters; the element value and the index.

list.eachWithIndex { v, i –>
    print "($i = $v) "
===> (0 = a) (1 = null) (2 = b) (3 = c)

Pretty neat . The sort of thing you’d expect from Ruby, Python or Perl.

contains( Object object ) – returns true if the collection contains the object given.

def nonnulls = [] 
list.eachWithIndex { v, i –>
    if  (idxs.contains(i)) nonnulls << v
println nonnulls
===> []

Woah!! So, ignoring the fact that this is a manufactured example, what I’ve done is pull the indexes of non-null elements out into a collection. Then I’ve iterated through the original collection seeing if the index of any particular element is in that list of non-null elements. If so, the element value gets appended it to the nonnulls collection. So kind of just round-tripping the process. I’d be expecting to see [ a, b, c ] but the cupboard was bare!

How can this be? Well, after a bit of investigation, it turns out that findIndexValues() puts java.lang.Long values into the collection, but the eachWithIndex() closure receives the index parameter as a java.lang.Integer. This means that I have to cast the Integer to a Long to properly do the contains() check.

def nonnulls = []
list.eachWithIndex { v, i ->
    if  (idxs.contains(i.toLong())) nonnulls << v
println nonnulls
===> [ a, b, c ]

This just seems wrong to me. Why not keep the default underlying numeric type as Long in both cases and avoid this sort of issue?



Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: