Groovy Makes Iteration Easy

| Comments

Out of the box, groovy gives you a number of powerful methods to iterate over lists and maps:

def fibList = [1, 1, 2, 3, 5]

fibList.each { println it }  // prints all of the numbers in the list
assert fibList.any { it == 3 }
assert fibList.every { it > 0 }
assert fibList.collect { it - 1 } == [0, 0, 1, 2, 4]
assert fibList.findAll { it > 1 && it < 5 } == [2, 3]
assert fibList.find { it > 1 } == 2
assert fibList.inject("fib: ") { str, val -> str << val }.toString() == "fib: 11235"

That’s really nice if you’re working with raw lists and maps, but what if you have a class that doesn’t extend list or map? How hard is it to empower that class with the groovy iteration methods? If this were Java, you’d likely need to implement an interface with these methods (and throw a “not implemented” exception for those you didn’t feel like taking the time to implement).

Since it’s not Java, but groovy (and you’ve read the title of this blog post :), you know it’s easy!

All you have to do is put a public method on your class called iterator() that returns an Iterator.

If you have a member variable that has an iterator you can expose, you can just use that:

class EvenFoo {
   private numbers = [2, 4, 6, 8, 10]
   Iterator iterator() {
       return numbers.iterator()
   }
}

def evens = new EvenFoo()
assert evens.each { println it } // prints even numbers 2 through 10
assert evens.findAll { it > 5 && it < 9 } == [6, 8]
assert evens.any { it == 4 }
assert evens.every { it > -1 }
assert evens.collect { it } == [2, 4, 6, 8, 10]
assert evens.inject("evens: ") { str, val -> str << val }.toString() == "evens: 246810"

You can also use the power of groovy to implement a subset of the Iterator interface using a map (just define hasNext and next). Return that, and it’s just as good as a regular Iterator from groovy’s perspective (duck typing rocks!):

class EvenFoo {
   def i = 0
   def max = 10
   Iterator iterator() {
       i = 0
       return [ hasNext: { i < max }, next: { i += 2 } ] as Iterator
   }
}

def evens = new EvenFoo()
assert evens.each { println it } // prints even numbers 2 through 10
assert evens.findAll { it > 5 && it < 9 } == [6, 8]
assert evens.any { it == 4 }
assert evens.every { it > -1 }
assert (evens.collect { it }) == [2, 4, 6, 8, 10]
assert evens.inject("evens: ") { str, val -> str << val }.toString() == "evens: 246810"

This is a contrived example, but I’ve run into a number of situations where I want to iterate over something that isn’t a list or map. It can be especially useful in lazy loading situations (such as a file/directory scanner or a SAX parser).

Comments