12 Groovy each() Loop Examples to Iterate Like a Pro

The Groovy each() method lets you iterate with elegant closures. See 12 practical examples covering lists, maps, strings, and more. Tested on Groovy 5.x.

“Iteration is the mother of mastery. In Groovy, each() makes that mastery effortless.”

Venkat Subramaniam, Programming Groovy 2

Last Updated: March 2026 | Tested on: Groovy 5.x, Java 17+ | Difficulty: Beginner to Intermediate | Reading Time: 18 minutes

If you’ve been writing for loops in Groovy the way you did in Java, it’s time for an upgrade. The groovy each method is the idiomatic way to iterate over collections, maps, strings, ranges, and pretty much anything iterable. It’s cleaner, more expressive, and feels like Groovy was built around it – because it was.

The each() method takes a closure as its argument and executes that closure for every element in the collection. No index management. No off-by-one errors. Just hand it a block of code and let Groovy do the rest. This groovy each loop tutorial walks you through 12 battle-tested examples that cover every scenario you’ll encounter in real projects.

This guide covers how to iterate lists, maps, strings, and ranges using each(), eachWithIndex(), and reverseEach(). You’ll also understand when not to use each() – because there are situations where for, times(), or collect() are the better choice.

What Is Groovy each()?

The each() method is added to Java’s collections by the Groovy Development Kit (GDK). It iterates over every element in a collection and passes each element to a closure you provide. Think of it as a more readable forEach – but available on everything from lists and maps to strings and file lines.

According to the official GDK documentation, each() always returns the original collection, not the results of the closure. This is important – it means each() is designed for side effects (printing, logging, modifying external state), not for transforming data.

Key Points:

  • each() accepts a closure with one parameter (the current element)
  • eachWithIndex() gives you both the element and its index
  • reverseEach() iterates backward through the collection
  • The implicit parameter it is used when you don’t name the closure parameter
  • each() returns the original collection, not the closure results
  • You cannot break out of each() – use find() or a traditional for loop instead

Syntax and Basic Usage

The basic syntax of each() is simple. You call it on any iterable and pass a closure:

each() Syntax

// Basic syntax
collection.each { element ->
    // do something with element
}

// Using implicit 'it' parameter
collection.each {
    // 'it' refers to current element
}

// eachWithIndex syntax
collection.eachWithIndex { element, index ->
    // access both element and its position
}

// reverseEach syntax
collection.reverseEach { element ->
    // iterate backward
}

When you don’t name the closure parameter, Groovy automatically provides the implicit variable it. For simple one-liners, using it keeps your code compact. For anything more complex, naming your parameter makes the code more readable.

For maps, each() can accept either one parameter (a Map.Entry) or two parameters (key and value separately):

Map each() Syntax

// One parameter: Map.Entry
map.each { entry ->
    println "${entry.key} = ${entry.value}"
}

// Two parameters: key and value
map.each { key, value ->
    println "${key} = ${value}"
}

12 Practical each() Examples

Let’s work through 12 examples that cover everything from basic list iteration to real-world data processing. Every example is tested on Groovy 5.x.

Example 1: each() on Lists

The most common use case – iterating over a list. You can use the implicit it parameter or name it explicitly:

Example 1: each() on Lists

def fruits = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry']

// Using implicit 'it'
println "=== Using implicit 'it' ==="
fruits.each {
    println "Fruit: ${it}"
}

// Using named parameter
println "\n=== Using named parameter ==="
fruits.each { fruit ->
    println "I like ${fruit.toLowerCase()}"
}

// Chaining - each() returns the original list
println "\n=== Chaining ==="
def result = fruits.each { print "${it} " }
println "\nReturned: ${result.class.simpleName}"

Output

=== Using implicit 'it' ===
Fruit: Apple
Fruit: Banana
Fruit: Cherry
Fruit: Date
Fruit: Elderberry

=== Using named parameter ===
I like apple
I like banana
I like cherry
I like date
I like elderberry

=== Chaining ===
Apple Banana Cherry Date Elderberry
Returned: ArrayList

Notice that each() returns the original list, not a transformed version. This is one of the key differences between each() and collect() that we’ll cover later.

Example 2: each() on Maps (Key-Value Pairs)

When you call each() on a map, you can destructure the entry into key and value parameters. This is where Groovy’s each loop really shines compared to Java’s verbose entrySet().forEach():

Example 2: each() on Maps

def scores = [Alice: 95, Bob: 82, Charlie: 91, Diana: 88]

// Two parameters: key and value
println "=== Key-Value parameters ==="
scores.each { name, score ->
    println "${name} scored ${score}"
}

// Single parameter: Map.Entry
println "\n=== Map.Entry parameter ==="
scores.each { entry ->
    println "${entry.key}: ${entry.value >= 90 ? 'A' : 'B'}"
}

// Using implicit 'it' (it is a Map.Entry)
println "\n=== Implicit 'it' ==="
scores.each {
    println "${it.key} -> ${it.value}"
}

Output

=== Key-Value parameters ===
Alice scored 95
Bob scored 82
Charlie scored 91
Diana scored 88

=== Map.Entry parameter ===
Alice: A
Bob: B
Charlie: A
Diana: B

=== Implicit 'it' ===
Alice -> 95
Bob -> 82
Charlie -> 91
Diana -> 88

Using two parameters (key, value) is the cleanest approach for map iteration. It reads almost like English: “for each name and score in the scores map.”

Example 3: eachWithIndex() – When You Need the Position

Sometimes you need both the element and its position. That’s what eachWithIndex() is for. The index is always the last parameter in the closure:

Example 3: eachWithIndex()

def languages = ['Groovy', 'Java', 'Kotlin', 'Scala', 'Clojure']

// eachWithIndex on a list
println "=== List with index ==="
languages.eachWithIndex { lang, idx ->
    println "${idx + 1}. ${lang}"
}

// eachWithIndex on a map
println "\n=== Map with index ==="
def config = [host: 'localhost', port: '8080', protocol: 'https']
config.eachWithIndex { entry, idx ->
    println "[${idx}] ${entry.key} = ${entry.value}"
}

// eachWithIndex with key, value, index
println "\n=== Map with key, value, index ==="
config.eachWithIndex { key, value, idx ->
    println "#${idx}: ${key} => ${value}"
}

Output

=== List with index ===
1. Groovy
2. Java
3. Kotlin
4. Scala
5. Clojure

=== Map with index ===
[0] host = localhost
[1] port = 8080
[2] protocol = https

=== Map with key, value, index ===
#0: host => localhost
#1: port => 8080
#2: protocol => https

For maps, eachWithIndex() can take either two parameters (entry, index) or three parameters (key, value, index). The three-parameter version is more readable when you need all three values.

Example 4: each() on Strings

Groovy strings are iterable, so you can call each() directly on them. Each iteration gives you a single character as a String (not a char):

Example 4: each() on Strings

def word = "GROOVY"

// Iterate each character
println "=== Characters ==="
word.each { ch ->
    print "${ch}-"
}
println()

// Count vowels using each
def vowelCount = 0
def vowels = 'AEIOU'
word.each { ch ->
    if (vowels.contains(ch)) {
        vowelCount++
    }
}
println "Vowels in '${word}': ${vowelCount}"

// eachWithIndex on a string
println "\n=== Characters with index ==="
"Hello".eachWithIndex { ch, idx ->
    println "  Position ${idx}: '${ch}' (ASCII: ${(int)ch.charAt(0)})"
}

Output

=== Characters ===
G-R-O-O-V-Y-
Vowels in 'GROOVY': 2

=== Characters with index ===
  Position 0: 'H' (ASCII: 72)
  Position 1: 'e' (ASCII: 101)
  Position 2: 'l' (ASCII: 108)
  Position 3: 'l' (ASCII: 108)
  Position 4: 'o' (ASCII: 111)

This is significantly cleaner than Java’s toCharArray() loop. And because Groovy treats each character as a one-character String, you can call String methods directly on it without casting.

Example 5: each() on Ranges

Groovy ranges are first-class iterable objects, so each() works on them naturally. This is an alternative to the times() method when you need a specific start and end:

Example 5: each() on Ranges

// Inclusive range
println "=== Inclusive range (1..5) ==="
(1..5).each { num ->
    print "${num} "
}
println()

// Exclusive range
println "\n=== Exclusive range (1..<5) ==="
(1..<5).each { num ->
    print "${num} "
}
println()

// Character range
println "\n=== Character range ==="
('A'..'F').each { letter ->
    print "${letter} "
}
println()

// Reverse range
println "\n=== Reverse range (5..1) ==="
(5..1).each { num ->
    print "${num} "
}
println()

// Practical: multiplication table
println "\n=== 7x Table ==="
(1..10).each { n ->
    println "7 x ${n} = ${7 * n}"
}

Output

=== Inclusive range (1..5) ===
1 2 3 4 5

=== Exclusive range (1..<5) ===
1 2 3 4

=== Character range ===
A B C D E F

=== Reverse range (5..1) ===
5 4 3 2 1

=== 7x Table ===
7 x 1 = 7
7 x 2 = 14
7 x 3 = 21
7 x 4 = 28
7 x 5 = 35
7 x 6 = 42
7 x 7 = 49
7 x 8 = 56
7 x 9 = 63
7 x 10 = 70

Character ranges are particularly useful for generating alphabetic sequences or validating that a character falls within a specific range. And yes, reverse ranges work exactly as you’d expect.

Example 6: each() on File Lines

One of Groovy’s most practical features is file handling with each(). You can iterate over lines in a file without manually managing readers or streams:

Example 6: each() on File Lines

// Create a sample file for demonstration
def tempFile = File.createTempFile('groovy-demo', '.txt')
tempFile.text = '''Line one: Hello Groovy
Line two: each() is powerful
Line three: Iteration made easy
Line four: No boilerplate needed
Line five: Clean and simple'''

// Iterate over each line
println "=== Reading file lines ==="
tempFile.eachLine { line ->
    println "  > ${line}"
}

// eachLine with line number
println "\n=== With line numbers ==="
tempFile.eachLine { line, lineNum ->
    println "  ${lineNum}: ${line}"
}

// Only process lines matching a pattern
println "\n=== Lines containing 'each' ==="
tempFile.eachLine { line ->
    if (line.contains('each')) {
        println "  MATCH: ${line}"
    }
}

// Clean up
tempFile.delete()

Output

=== Reading file lines ===
  > Line one: Hello Groovy
  > Line two: each() is powerful
  > Line three: Iteration made easy
  > Line four: No boilerplate needed
  > Line five: Clean and simple

=== With line numbers ===
  1: Line one: Hello Groovy
  2: Line two: each() is powerful
  3: Line three: Iteration made easy
  4: Line four: No boilerplate needed
  5: Line five: Clean and simple

=== Lines containing 'each' ===
  MATCH: Line two: each() is powerful

The eachLine() method automatically handles opening and closing the file reader. Compare this to Java’s try-with-resources and BufferedReader – Groovy eliminates all that ceremony.

Example 7: reverseEach() – Iterating Backward

Need to process a collection from the last element to the first? reverseEach() is your answer. No need to reverse the collection first:

Example 7: reverseEach()

def stack = ['First', 'Second', 'Third', 'Fourth', 'Fifth']

// Forward iteration
println "=== Forward ==="
stack.each { println "  ${it}" }

// Reverse iteration
println "\n=== Reverse ==="
stack.reverseEach { println "  ${it}" }

// Practical: building a countdown
println "\n=== Countdown ==="
(1..5).reverseEach { num ->
    println "  ${num}..."
}
println "  Liftoff!"

// reverseEach on a list
println "\n=== Reverse letters ==="
def letters = ['a', 'b', 'c', 'd', 'e']
print "  Reversed: "
letters.reverseEach { print "$it " }
println()

Output

=== Forward ===
  First
  Second
  Third
  Fourth
  Fifth

=== Reverse ===
  Fifth
  Fourth
  Third
  Second
  First

=== Countdown ===
  5...
  4...
  3...
  2...
  1...
  Liftoff!

=== Reverse letters ===
  Reversed: e d c b a

While you could achieve the same thing with collection.reverse().each{}, reverseEach() is more efficient because it doesn’t create a reversed copy of the collection.

Example 8: Nested each() Loops

You can nest each() calls just like nested for loops. The key is to name your closure parameters clearly to avoid confusion with it:

Example 8: Nested each()

// Nested lists
def matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

println "=== Matrix ==="
matrix.each { row ->
    row.each { cell ->
        print "${String.format('%3d', cell)}"
    }
    println()
}

// Nested maps
def departments = [
    Engineering: ['Alice', 'Bob'],
    Marketing:   ['Charlie', 'Diana'],
    Sales:       ['Eve', 'Frank']
]

println "\n=== Department Roster ==="
departments.each { dept, members ->
    println "${dept}:"
    members.each { member ->
        println "  - ${member}"
    }
}

Output

=== Matrix ===
  1  2  3
  4  5  6
  7  8  9

=== Department Roster ===
Engineering:
  - Alice
  - Bob
Marketing:
  - Charlie
  - Diana
Sales:
  - Eve
  - Frank

Always use named parameters in nested each() calls. If you use it in both levels, the inner it shadows the outer one and you’ll get confusing bugs.

Example 9: each() with Side Effects

Since each() returns the original collection, it’s purpose-built for side effects – modifying external variables, writing to files, updating databases, or building up results:

Example 9: each() with Side Effects

def numbers = [10, 25, 37, 42, 55, 68, 71, 89, 93]

// Accumulating results
def sum = 0
def evenCount = 0
def oddNumbers = []

numbers.each { n ->
    sum += n
    if (n % 2 == 0) {
        evenCount++
    } else {
        oddNumbers << n
    }
}

println "Sum: ${sum}"
println "Even count: ${evenCount}"
println "Odd numbers: ${oddNumbers}"

// Building a report string
def report = new StringBuilder()
report.append("=== Sales Report ===\n")

def sales = [Jan: 1200, Feb: 1800, Mar: 1500, Apr: 2100]
def total = 0

sales.each { month, amount ->
    report.append("${month}: \$${amount}\n")
    total += amount
}
report.append("Total: \$${total}")

println "\n${report}"

Output

Sum: 490
Even count: 3
Odd numbers: [25, 37, 55, 71, 89, 93]

=== Sales Report ===
Jan: $1200
Feb: $1800
Mar: $1500
Apr: $2100
Total: $6600

This pattern – iterating with each() to accumulate or build something – is common, but be aware that inject() (also known as reduce/fold) is often a cleaner choice for accumulation. Use each() when you have multiple side effects happening per iteration.

Example 10: Real-World – Processing JSON-like Data

Here’s a realistic scenario: processing a list of records (like parsed JSON) and generating a formatted report:

Example 10: Real-World Data Processing

// Simulated data (like parsed JSON)
def employees = [
    [name: 'Alice',   dept: 'Engineering', salary: 95000],
    [name: 'Bob',     dept: 'Engineering', salary: 88000],
    [name: 'Charlie', dept: 'Marketing',   salary: 72000],
    [name: 'Diana',   dept: 'Marketing',   salary: 78000],
    [name: 'Eve',     dept: 'Engineering', salary: 102000],
    [name: 'Frank',   dept: 'Sales',       salary: 65000]
]

// Group salaries by department
def deptTotals = [:]
def deptCounts = [:]

employees.each { emp ->
    def dept = emp.dept
    deptTotals[dept] = (deptTotals[dept] ?: 0) + emp.salary
    deptCounts[dept] = (deptCounts[dept] ?: 0) + 1
}

// Print department averages
println "=== Department Salary Averages ==="
deptTotals.each { dept, total ->
    def avg = total / deptCounts[dept]
    println "${dept}: \$${String.format('%,.0f', avg)} avg (${deptCounts[dept]} employees)"
}

// Find high earners
println "\n=== Employees earning > \$90k ==="
employees.each { emp ->
    if (emp.salary > 90000) {
        println "  ${emp.name} (${emp.dept}): \$${String.format('%,d', emp.salary)}"
    }
}

Output

=== Department Salary Averages ===
Engineering: $95,000 avg (3 employees)
Marketing: $75,000 avg (2 employees)
Sales: $65,000 avg (1 employees)

=== Employees earning > $90k ===
  Alice (Engineering): $95,000
  Eve (Engineering): $102,000

This is a pattern you’ll see constantly in Groovy scripts, Jenkins pipelines, and Gradle build files. The each() method keeps the code flat and readable, even when the logic inside is nontrivial.

Example 11: each() on Sets, Queues, and Arrays

The each() method works on any iterable – not just lists and maps. Here’s how it works with sets, arrays, and other collection types:

Example 11: each() on Various Collections

// Sets - no duplicates, unordered
def uniqueTags = ['groovy', 'java', 'groovy', 'kotlin', 'java'] as Set
println "=== Set (unique values) ==="
uniqueTags.each { tag ->
    println "  Tag: ${tag}"
}

// Arrays
def numbers = [10, 20, 30, 40, 50] as int[]
println "\n=== Array ==="
numbers.each { num ->
    print "${num} "
}
println()

// LinkedHashSet preserves insertion order
def orderedSet = new LinkedHashSet(['first', 'second', 'third'])
println "\n=== LinkedHashSet (ordered) ==="
orderedSet.each { item ->
    println "  ${item}"
}

// each on null-safe collections
def maybeNull = null
println "\n=== Null-safe iteration ==="
maybeNull?.each { item ->
    println "  ${item}"  // Never executes
}
println "  (No output - null was safely skipped)"

Output

=== Set (unique values) ===
  Tag: groovy
  Tag: java
  Tag: kotlin

=== Array ===
10 20 30 40 50

=== LinkedHashSet (ordered) ===
  first
  second
  third

=== Null-safe iteration ===
  (No output - null was safely skipped)

The null-safe operator ?. is critical in production code. Without it, calling each() on a null reference throws a NullPointerException. Always use maybeNull?.each{} when the collection might be null.

Example 12: Building an HTML Report with each()

Let’s combine several each() patterns into a real-world scenario – generating an HTML table from structured data:

Example 12: Building an HTML Report

def servers = [
    [name: 'web-01',  status: 'UP',   cpu: 45, memory: 62],
    [name: 'web-02',  status: 'UP',   cpu: 72, memory: 81],
    [name: 'db-01',   status: 'UP',   cpu: 38, memory: 55],
    [name: 'db-02',   status: 'DOWN', cpu: 0,  memory: 0],
    [name: 'cache-01',status: 'UP',   cpu: 15, memory: 30]
]

def html = new StringBuilder()
html.append('<table>\n')
html.append('  <tr><th>Server</th><th>Status</th><th>CPU%</th><th>Memory%</th></tr>\n')

def upCount = 0
def downCount = 0

servers.each { server ->
    def statusClass = server.status == 'UP' ? 'green' : 'red'
    html.append("  <tr>")
    html.append("<td>${server.name}</td>")
    html.append("<td style='color:${statusClass}'>${server.status}</td>")
    html.append("<td>${server.cpu}%</td>")
    html.append("<td>${server.memory}%</td>")
    html.append("</tr>\n")

    if (server.status == 'UP') upCount++
    else downCount++
}

html.append('</table>')

println html.toString()
println "\nSummary: ${upCount} UP, ${downCount} DOWN"

Output

<table>
  <tr><th>Server</th><th>Status</th><th>CPU%</th><th>Memory%</th></tr>
  <tr><td>web-01</td><td style='color:green'>UP</td><td>45%</td><td>62%</td></tr>
  <tr><td>web-02</td><td style='color:green'>UP</td><td>72%</td><td>81%</td></tr>
  <tr><td>db-01</td><td style='color:green'>UP</td><td>38%</td><td>55%</td></tr>
  <tr><td>db-02</td><td style='color:red'>DOWN</td><td>0%</td><td>0%</td></tr>
  <tr><td>cache-01</td><td style='color:green'>UP</td><td>15%</td><td>30%</td></tr>
</table>

Summary: 4 UP, 1 DOWN

This pattern is incredibly common in Groovy-based reporting scripts, Jenkins pipeline notifications, and Gradle custom tasks. The combination of each() with a StringBuilder gives you complete control over the output format.

each() vs for Loop Performance

A question that comes up constantly: “Is each() slower than a for loop?” The honest answer: yes, slightly, because each() creates a closure object. But in practice, the difference is negligible for 99% of use cases.

each() vs for Performance

def list = (1..100000).toList()

// Benchmark: for loop
def start1 = System.nanoTime()
def sum1 = 0
for (n in list) {
    sum1 += n
}
def time1 = (System.nanoTime() - start1) / 1_000_000

// Benchmark: each()
def start2 = System.nanoTime()
def sum2 = 0
list.each { n ->
    sum2 += n
}
def time2 = (System.nanoTime() - start2) / 1_000_000

println "for loop:  ${sum1} in ${time1} ms"
println "each():    ${sum2} in ${time2} ms"
println "Same result: ${sum1 == sum2}"
println "\nVerdict: Difference is minimal for most applications."
println "Use each() for readability. Use for only when"
println "you need break/continue or maximum raw speed."

Output

for loop:  705082704 in 42 ms
each():    705082704 in 48 ms
Same result: true

Verdict: Difference is minimal for most applications.
Use each() for readability. Use for only when
you need break/continue or maximum raw speed.

The performance difference is typically under 15% and often much less. Unless you’re processing millions of elements in a tight loop where every millisecond matters, prefer each() for its readability and Groovy-idiomatic style.

Featurefor Loopeach()
SpeedSlightly fasterSlightly slower (closure overhead)
break/continueSupportedNot supported
ReadabilityGoodExcellent (idiomatic Groovy)
Return valueN/AReturns original collection
Index accessManualUse eachWithIndex()

each() vs collect() – When to Use Which

This is probably the most important distinction in Groovy collection processing. Both iterate over elements, but they serve fundamentally different purposes:

each() vs collect()

def prices = [10.0, 25.5, 33.0, 42.99, 15.75]

// WRONG: Using each() to transform data
def taxed1 = []
prices.each { price ->
    taxed1 << price * 1.1
}
println "each() + manual list: ${taxed1}"

// RIGHT: Using collect() to transform data
def taxed2 = prices.collect { price ->
    price * 1.1
}
println "collect():            ${taxed2}"

// each() returns the ORIGINAL collection
def result1 = prices.each { it * 2 }
println "\neach() returns:    ${result1}"   // Original prices!

// collect() returns a NEW collection
def result2 = prices.collect { it * 2 }
println "collect() returns: ${result2}"     // Doubled prices!

Output

each() + manual list: [11.0, 28.05, 36.3, 47.289, 17.325]
collect():            [11.0, 28.05, 36.3, 47.289, 17.325]

each() returns:    [10.0, 25.5, 33.0, 42.99, 15.75]
collect() returns: [20.0, 51.0, 66.0, 85.98, 31.5]

Rule of thumb:

  • Use each() for side effects – printing, logging, modifying external state, writing to files
  • Use collect() for transformations – when you need a new list derived from the original
  • If you find yourself building a list inside each(), you almost certainly should be using collect() instead

Breaking Out of each() – You Can’t!

This catches every Groovy beginner at some point. You cannot use break or continue inside each(). The closure is a separate block of code – it’s not a loop statement that the JVM can break out of. Trying it will give you a compilation error.

Cannot Break from each()

def numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

// THIS WON'T COMPILE:
// numbers.each { n ->
//     if (n == 5) break   // ERROR: break outside of loop
// }

// SOLUTION 1: Use find() to stop at first match
println "=== find() - stops at first match ==="
def found = numbers.find { n ->
    println "  Checking: ${n}"
    return n == 5
}
println "Found: ${found}"

// SOLUTION 2: Use any() to stop when condition is true
println "\n=== any() - stops at first true ==="
def hasEven = numbers.any { n ->
    println "  Checking: ${n}"
    return n % 2 == 0
}
println "Has even: ${hasEven}"

// SOLUTION 3: Use a traditional for loop
println "\n=== for with break ==="
for (n in numbers) {
    if (n > 5) break
    println "  Processing: ${n}"
}

Output

=== find() - stops at first match ===
  Checking: 1
  Checking: 2
  Checking: 3
  Checking: 4
  Checking: 5
Found: 5

=== any() - stops at first true ===
  Checking: 1
  Checking: 2
Has even: true

=== for with break ===
  Processing: 1
  Processing: 2
  Processing: 3
  Processing: 4
  Processing: 5

Notice how find() stops iterating the moment the closure returns true, and any() does the same. These are the idiomatic alternatives to breaking out of an iteration. If you truly need early termination with complex logic, use a for loop instead of each().

Pro Tip: A common workaround is using return inside each(). But be careful – return only exits the current closure invocation (like continue in a for loop), it does NOT stop the iteration. The next element will still be processed.

return acts like continue, not break

println "=== return inside each() acts like continue ==="
[1, 2, 3, 4, 5].each { n ->
    if (n == 3) return   // Skips 3 but continues to 4, 5
    println "  Processing: ${n}"
}

Output

=== return inside each() acts like continue ===
  Processing: 1
  Processing: 2
  Processing: 4
  Processing: 5

Edge Cases and Best Practices

Best Practices Summary

DO:

  • Use each() for side effects: printing, logging, writing files, modifying external state
  • Name your closure parameters in nested each() calls to avoid it shadowing
  • Use ?. (null-safe operator) when the collection might be null
  • Use eachWithIndex() when you need the element position
  • Use reverseEach() instead of reverse().each() – it’s more efficient

DON’T:

  • Use each() to build a new list – use collect() instead
  • Try to use break inside each() – use find(), any(), or a for loop
  • Modify the collection you’re iterating over inside each() – you’ll get a ConcurrentModificationException
  • Confuse return inside each() with breaking – it only skips the current iteration

Edge Case: Modifying During Iteration

Modifying During Iteration – Dangerous!

def items = ['a', 'b', 'c', 'd']

// WRONG: This throws ConcurrentModificationException
try {
    items.each { item ->
        if (item == 'b') {
            items.remove(item)  // Modifying during iteration!
        }
    }
} catch (ConcurrentModificationException e) {
    println "Error: ${e.class.simpleName} - Cannot modify list during each()"
}

// RIGHT: Use removeAll or filter first
def safeItems = ['a', 'b', 'c', 'd']
safeItems.removeAll { it == 'b' }
println "Safe result: ${safeItems}"

// ALSO RIGHT: Iterate over a copy
def original = ['a', 'b', 'c', 'd']
original.collect().each { item ->
    if (item == 'b') original.remove(item)
}
println "Copy-iterate result: ${original}"

Output

Error: ConcurrentModificationException - Cannot modify list during each()
Safe result: [a, c, d]
Copy-iterate result: [a, c, d]

Edge Case: Empty Collections

each() on Empty Collections

// each() on empty collections is safe - closure never executes
[].each { println "This never prints" }
[:].each { k, v -> println "This never prints either" }
''.each { println "Empty string? No iterations" }

println "All empty iterations completed safely."
println "No NullPointerException. No errors."

Output

All empty iterations completed safely.
No NullPointerException. No errors.

Empty collections are perfectly safe with each(). The closure simply never executes. But null collections will throw a NullPointerException, so always use the null-safe operator ?. when there’s a chance the collection is null.

Conclusion

The Groovy each() method is the backbone of idiomatic collection processing. It replaces verbose Java for-each loops with clean, closure-based iteration that reads almost like natural language. We covered 12 practical examples spanning lists, maps, strings, ranges, files, and real-world data processing scenarios.

The key insight is knowing when to use each(). Use it for side effects – printing, logging, building reports, modifying external state. When you need to transform data, reach for collect(). When you need early termination, use find() or a traditional for loop. And when you need the index, use eachWithIndex().

In the next post, we’ll look at the times() method – another Groovy-idiomatic way to run code a fixed number of times without managing counters. And if you haven’t yet, check out how Groovy’s switch statement works with closures for pattern matching.

Summary

  • each() iterates over any iterable and passes each element to a closure
  • each() returns the original collection – it’s for side effects, not transformations
  • Use eachWithIndex() when you need the element position
  • Use collect() instead of each() when building a new list
  • You cannot break from each() – use find() or a for loop
  • Always use ?. before each() on potentially null collections

If you also work with build tools, CI/CD pipelines, or cloud CLIs, check out Command Playground to practice 105+ CLI tools directly in your browser — no install needed.

Up next: Groovy times() – Repeat Code N Times

Frequently Asked Questions

What is the difference between each() and collect() in Groovy?

each() iterates over a collection and executes a closure for side effects (printing, logging, modifying external state). It returns the original collection. collect() also iterates but builds and returns a NEW list from the closure’s return values. Use each() for side effects and collect() for transformations.

Can you break out of a Groovy each() loop?

No. You cannot use break or continue inside each() because the closure is not a traditional loop construct. To stop iteration early, use find() (stops at first match), any() (stops at first true), or switch to a traditional for loop that supports break.

What does ‘it’ mean inside a Groovy each() closure?

‘it’ is the implicit parameter name that Groovy assigns to a single-parameter closure when you don’t declare a parameter explicitly. Inside each(), ‘it’ refers to the current element being iterated. For readability in complex closures, name the parameter explicitly like: list.each { item -> … }.

How do I iterate over a map with each() in Groovy?

You can use either one parameter (a Map.Entry) or two parameters (key, value). Example with two parameters: map.each { key, value -> println${key} = ${value}” }. For index access, use eachWithIndex with three parameters: map.eachWithIndex { key, value, index -> … }.

Is Groovy each() slower than a for loop?

Slightly, because each() creates a closure object. However, the performance difference is typically under 15% and is negligible for most applications. Prefer each() for its readability and idiomatic Groovy style. Only switch to a for loop when you need break/continue or are processing millions of elements in performance-critical code.

Previous in Series: Groovy for Loop – All Loop Variations Explained

Next in Series: Groovy times() – Repeat Code N Times

Related Topics You Might Like:

This post is part of the Groovy & Grails Cookbook series on TechnoScripts.com

RahulAuthor posts

Avatar for Rahul

Rahul is a passionate IT professional who loves to sharing his knowledge with others and inspiring them to expand their technical knowledge. Rahul's current objective is to write informative and easy-to-understand articles to help people avoid day-to-day technical issues altogether. Follow Rahul's blog to stay informed on the latest trends in IT and gain insights into how to tackle complex technical issues. Whether you're a beginner or an expert in the field, Rahul's articles are sure to leave you feeling inspired and informed.

No comment

Leave a Reply

Your email address will not be published. Required fields are marked *