I guess you may heard about Groovy’s Collection.each(Closure cl) method - it was introduced 15 years ago [1] and it was a great alternative for a good old for-loop, for-each or even using an iterator approach. You may also heard, that you should not overuse it, because creating a closure to do such simple operation like collection iteration is an overhead. But what if I tell you that nothing could be further from the truth - Groovy’s each method may be faster than iterator or Java’s for-each. Sounds interesting? Enjoy the reading!


Before we dig deeper, let’s define the initial conditions. We are going to measure an execution time of a few different iteration variants. Each variant will do the same simple operation - it will accumulate the sum of numbers from 0 to 10 millions. We will do it using AtomicLong object and in the most imperative way possible - it is not about finding the most efficient sum operation - it is about giving all variants a simple job to do, so we can focus on iteration process and draw a conclusion after all.

We are going to use JMH benchmark tool and its Gradle plugin.

What methods are we going to test? We will compare:

  • Groovy’s each method

  • Java’s for-loop approach

  • Java’s for-each approach

  • Java’s iterator approach

  • Java 8 forEach method using closure

  • Java 8 forEach method using anonymous class

This test uses Groovy 2.4 which does not support Java 8 lambda expressions - that is why we will use Java 8 forEach method with an anonymous class instead.

The code

Let’s start with build.gradle file

Listing 1. build.gradle
buildscript {
  repositories {
    maven {
      url "https://plugins.gradle.org/m2/"
  dependencies {
    classpath "me.champeau.gradle:jmh-gradle-plugin:0.4.7"

apply plugin: "idea"
apply plugin: "groovy"
apply plugin: "me.champeau.gradle.jmh"

repositories {

dependencies {
    compile 'org.codehaus.groovy:groovy-all:2.4.12'

jmh {
    include = ['bench\\.*'] (1)
    benchmarkMode = ['avgt'] (2)
    timeUnit = 'ms' (3)
    iterations = 120 (4)
    timeOnIteration = '1s' (5)
    warmup = '60s' (6)
    warmupIterations = 1 (7)
    batchSize = 1
    fork = 1
1Load test classes from bench. package
2Use average execution time measurement
3Measure time in milliseconds
4Execute 120 iterations
5Each iteration is limited to 1 second
6Warm-up for 60 seconds
7Use a single iteration to warm-up

And now let’s take a look at our benchmark test class:

Listing 2. src/jmh/groovy/bench/GroovyIterationBenchmarks.groovy
package bench

import org.openjdk.jmh.annotations.Benchmark
import org.openjdk.jmh.annotations.Scope
import org.openjdk.jmh.annotations.State

import java.util.concurrent.atomic.AtomicLong
import java.util.function.Consumer

class GroovyIterationBenchmarks {

    final List<Integer> numbers = 0..10_000_000 (1)

    AtomicLong eachTest() { (2)
        final AtomicLong result = new AtomicLong()
        numbers.each { result.addAndGet(it) }
        return result

    AtomicLong forEachTest() { (3)
        final AtomicLong result = new AtomicLong()
        for (int number : numbers) {
        return result

    AtomicLong forLoopTest() { (4)
        final AtomicLong result = new AtomicLong()
        for (int i = 0; i < numbers.size(); i++) {
        return result

    AtomicLong iteratorTest() { (5)
        final AtomicLong result = new AtomicLong()
        final Iterator<Integer> iterator = numbers.iterator()
        while (iterator.hasNext()) {
        return result

    AtomicLong java8ForEachWithClosureTest() { (6)
        final AtomicLong result = new AtomicLong()
        numbers.forEach { result.addAndGet((int) it) }
        return result

    AtomicLong java8ForEachWithAnonymousClassTest() { (7)
        final AtomicLong result = new AtomicLong()
        numbers.forEach(new Consumer<Integer>() {
            void accept(Integer number) {
        return result
1List of numbers from 0 to 10 millions
2Groovy each {} test case
3Old Java for-each loop test case
4Old Java for-loop test case
5Old Java iterator test case
6Java 8 forEach() test case with closure in place of a lambda expression
7Java 8 forEach() test case with an anonymous class in place of lambda expression
Above example can be cloned from https://github.com/wololock/groovy-jmh

The results

We are ready to execute the test using Gradle:

./gradlew jmh

And after about 7 minutes we can take a look at the results:

Listing 3. Benchmark results for 10M collection size
Benchmark                                       Mode  Cnt    Score   Error  Units
GroovyBench.eachTest                            avgt  120  652,584 ± 2,064  ms/op
GroovyBench.forEachTest                         avgt  120  221,790 ± 1,675  ms/op
GroovyBench.forLoopTest                         avgt  120  533,534 ± 2,521  ms/op
GroovyBench.iteratorTest                        avgt  120  369,492 ± 0,930  ms/op
GroovyBench.java8ForEachWithAnonymousClassTest  avgt  120  248,371 ± 2,803  ms/op
GroovyBench.java8ForEachWithClosureTest         avgt  120  785,309 ± 3,096  ms/op
  • The benchmarks shows clearly that using Groovy each with a closure is almost three times slower than good old Java for-each loop (653 ms versus 222 ms).

  • Java for-each and Java 8 forEach with anonymous class are pretty close - 222 ms versus 248 ms.

  • The slowest variant was Java 8 forEach with a closure in place of a lambda expression - it took 785 ms to execute (133 ms more than Groovy each).

No matter which variant won in this run it still feels like there is something wrong - iterating collection of 10 millions integers took 304 milliseconds at best, which is still quite slow. The reason of that is because we were testing Groovy’s dynamic method invocation which comes with some overhead. Let’s turn on static compilation and see how it works.

Laptop specs: JDK 1.8.0_162 (Java HotSpot™ 64-Bit Server VM, 25.162-b12), Groovy 2.4.12, Intel® Core™ i7-4900MQ CPU @ 2.80GHz (4 cores, cache size 8192 KB), 16 GB RAM, OS: Fedora 26 (64 bit)

The improvement: static compilation

Without further ado, let’s add @CompileStatic and @TypeChekced annotations to our GroovyBench class:

Listing 4. Enabling static compilation and type checks
package bench

import groovy.transform.CompileStatic
import groovy.transform.TypeChecked
import org.openjdk.jmh.annotations.Benchmark
import org.openjdk.jmh.annotations.Scope
import org.openjdk.jmh.annotations.State

import java.util.concurrent.atomic.AtomicLong
import java.util.function.Consumer

@CompileStatic (1)
@TypeChecked (2)
class GroovyBench {

    final List<Integer> numbers = 0..10_000_000

    // the same benchmark methods


Let’s run ./gradlew clean jmh and see the results:

Listing 5. Benchmark results for statically compiled Groovy code
Benchmark                                       Mode  Cnt    Score   Error  Units
GroovyBench.eachTest                            avgt  120   91,897 ± 0,346  ms/op
GroovyBench.forEachTest                         avgt  120   96,422 ± 0,550  ms/op
GroovyBench.forLoopTest                         avgt  120  139,119 ± 0,723  ms/op
GroovyBench.iteratorTest                        avgt  120  103,568 ± 0,648  ms/op
GroovyBench.java8ForEachWithAnonymousClassTest  avgt  120  102,460 ± 2,473  ms/op
GroovyBench.java8ForEachWithClosureTest         avgt  120  400,481 ± 1,036  ms/op

As you can see, enabling static compilation was a game changer! To sum it up:

  • Groovy each recorded the best result - 91.897 ms (previously: 652.584 ms)

  • The second best result belongs to Java for-each - 96.422 ms (previously: 221.790 ms)

  • Java 8 forEach and iterator recorded almost the same result - 102.460 ms and 103.568 ms accordingly.

  • And again the slowest result belongs to Java 8 forEach with a closure - 400.481 ms (previously: `785.309 ` ms)

As you can see Groovy each method with a closure can be faster than other variants when static compilation is enabled. But is it always like that? It depends.

Small collection size

Let’s run the same benchmark, but this time let’s limit the numbers of elements in the input list from 10 millions to 10 thousands and see what the results are.

Listing 6. Benchmark results for a list of size 10K
Benchmark                                       Mode  Cnt  Score    Error  Units
GroovyBench.eachTest                            avgt  120  0,087 ±  0,001  ms/op
GroovyBench.forEachTest                         avgt  120  0,079 ±  0,001  ms/op
GroovyBench.forLoopTest                         avgt  120  0,157 ±  0,002  ms/op
GroovyBench.iteratorTest                        avgt  120  0,079 ±  0,002  ms/op
GroovyBench.java8ForEachWithAnonymousClassTest  avgt  120  0,085 ±  0,001  ms/op
GroovyBench.java8ForEachWithClosureTest         avgt  120  0,402 ±  0,002  ms/op

Things got change as you can see.

  • For 10k size collection the best result gave Java for-each and iterator - 0.079 ms average.

  • Java 8 forEach with anonymous class was only 0.006 ms slower - 0.085 ms average.

  • Groovy each was only 0.008 ms slower than the best result - 0.087 ms average.

  • And again the slowest variant was Java 8 forEach with a closure - 0.402 ms average.

Even though Groovy each didn’t record the best result this time, it is still very close to the fastest variant.

Benchmarking Java

Before we close this article, let’s take a quick look at the Java benchmark results to get a better understanding how Groovy efficiency differs from Java. Below you can find results of Java benchmark test for two variants - old Java for-each and iterator:

Listing 7. Benchmark results for Java and 10M collection size
Benchmark                                       Mode  Cnt    Score   Error  Units
JavaBench.javaForEach                           avgt  120    8,839 ± 0,011  ms/op
JavaBench.javaIteratorTest                      avgt  120    8,865 ± 0,011  ms/op

Let’s compare the results:

  • Groovy for-each took in average 96.422 ms while Java did the same job in approximately 8.839 ms.

  • Groovy iterator test took in average 103.568 ms while Java did the same job in approximately 8.865 ms.


Now it is the good time to draw a conclusion.

  • When you program in statically compiled Groovy there is no difference if you use each with a closure or you stick to old for-each constructions that don’t require creating a closure.

  • When you program in a dynamic Groovy and you need more efficient iteration algorithm - extract code to a statically compiled class, otherwise you will lost a lot of milliseconds in case of a huge collections.

  • When you can choose between Groovy and Java and you need blazing fast solution - pick Java.

  • And remember that "premature optimization is root of all evil" [2] - before you start refactoring your iteration code make sure that switching from one variant to another will give you a real boost. Your application most probably spends most of the time on I/O (e.g. loading data from the database) and saving a millisecond here or there might not be worth the effort.

I hope you have enjoyed reading this blog post. Feel free to leave a comment in the section below, I would love to hear your opinion. Until the next time!

Looking for a good Groovy book?

Would you like to learn more about Groovy programming language? Maybe you are looking for a book recommendation? I can definitely recommend reading "Groovy In Action, 2nd edition" written by Dierk König, Paul King, and associates. This is a book 882 pages long that explains Groovy in details. It starts with explaining basic concepts of the language, datatypes, control structures and closures. It explains Groovy’s dynamic programming features, run-time and compile-time metaprogramming, AST transformations, and benefits from using Groovy as a statically compiled language.

It also covers things around the Groovy library - builders, GDK (Groovy Development Kit) enhancements, tools for accessing the database, working with XML and JSON, as well as interacting with various Web Services types (REST-based APIs, XML-RPC, or SOAP). And much, much more.

If you are looking for a comprehensive guide to the Groovy programming language and its rich ecosystem, this book is the right choice for you. I keep this book close to my desk so I can grab it and find interesting part during my daily work.

groovy in action 01
groovy in action 02
groovy in action 03

The above link is an Amazon’s affiliate link. If you decide to purchase a book through this link, I will earn a small commision (without affecting the regular price of the book). This way you can support the effort I put in writing the blog posts and sharing my passion to programming. Thanks for your understanding!

1. The first commit in the repository tree that mentions DefaultGroovyMethods.each(Object self, Closure cl) method comes from September 11th 2003

Thank you!

If you read up to this point - leave me a comment below. It means a lot to me to hear your opinion. Share this blog post on Twitter, Facebook, or LinkedIn, if you found it helpful.

I will be very grateful if you consider supporting my work and effort. I run this blog to share my experience, inspire, and help other people to become better problem solvers.

Szymon Stepniak


Groovista, Upwork's Top Rated freelancer, Toruń Java User Group founder, open source contributor, Stack Overflow addict, bedroom guitar player. I walk through e.printStackTrace() so you don't have to.