14 Essential Groovy For Loop Variations Every Developer Should Know

Every Groovy for loop variation is covered here with 14 tested examples. Classic for, for-in, ranges, each, and more. Complete guide for Groovy 5.x.

“Any fool can write a loop that works. A good developer writes a loop that’s readable, efficient, and idiomatic.”

Edsger Dijkstra, A Discipline of Programming

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

Loops are the backbone of programming. Processing a list of users, crunching numbers, building output strings – you’ll need a Groovy for loop at some point. And Groovy gives you more looping options than you might expect.

If you’re coming from Java, you already know the classic C-style for (int i = 0; i < n; i++) loop. Groovy supports that, but it also offers for-in loops with ranges, lists, maps, and strings. On top of that, Groovy’s GDK adds methods like each() and times() that make iteration feel effortless.

In this post, we’ll cover every Groovy for loop variation with 14 tested examples. Each example shows exactly which loop style to use and when. If you’re new to Groovy collections, start with our Groovy Collections Overview first.

What Is a For Loop in Groovy?

A Groovy for loop is a control flow statement that repeatedly executes a block of code. Groovy supports two main forms: the classic C-style for loop and the enhanced for-in loop. The for-in loop is the idiomatic Groovy choice because it works with ranges, lists, maps, strings, and any iterable object.

According to the official Groovy semantics documentation, Groovy’s for-in loop is more versatile than Java’s enhanced for loop. It supports Groovy ranges, any Iterable, arrays, maps (iterating over entries), and even characters in a string.

Key Points:

  • Classic C-style for (init; condition; update) works just like Java
  • for (item in collection) is the idiomatic Groovy form
  • Groovy ranges (1..10, 1..<10) make numeric loops clean and readable
  • Maps iterate as key-value entries automatically
  • The GDK adds each(), eachWithIndex(), and times() as alternatives
  • break and continue work with for loops but NOT with each()

Why Use For Loops in Groovy?

You might be wondering – if Groovy has each() and collect(), why bother with for loops at all? Great question. Here’s why for loops still matter:

  • Break and continue support: You can exit early with break or skip iterations with continue – something each() cannot do
  • Readability: For simple iterations, a for loop is immediately clear to any developer
  • Index control: C-style for loops give you precise control over start, end, and step values
  • Performance: For loops have slightly less overhead than closure-based methods
  • Java compatibility: Java developers joining your team will instantly understand for loops

That said, idiomatic Groovy code often prefers each() for simple iteration and times() for repeat-N-times scenarios. Use whichever makes your code clearest.

Groovy For Loop Syntax

Classic C-Style For Loop

C-Style Syntax

// Standard C-style for loop
for (int i = 0; i < n; i++) {
    // code
}

// Groovy style (def instead of int)
for (def i = 0; i < n; i++) {
    // code
}

For-In Loop (Idiomatic Groovy)

For-In Syntax

// With a range
for (i in 1..10) { }

// With a list
for (item in [1, 2, 3]) { }

// With a map
for (entry in map) { }

// With a string (iterates characters)
for (ch in "hello") { }

Comparison Table

SyntaxUse Casebreak/continueIndex Access
for (i = 0; i < n; i++)Precise index controlYesBuilt-in
for (item in collection)Iterate any iterableYesManual
for (i in 0..n)Range-based countingYesBuilt-in
collection.each { }Functional styleNoeachWithIndex
n.times { }Repeat N timesNoClosure param

14 Practical Groovy For Loop Examples

Example 1: Classic C-Style For Loop

Let’s start with the familiar C-style for loop. If you’re coming from Java or C, this will feel right at home. It gives you full control over the counter variable, the termination condition, and the step.

Example 1: Classic C-Style For Loop

// Classic C-style for loop
println "Counting from 1 to 5:"
for (int i = 1; i <= 5; i++) {
    print "${i} "
}
println()

// Counting down
println "\nCountdown:"
for (int i = 5; i >= 1; i--) {
    print "${i} "
}
println()

// Step by 2
println "\nEven numbers up to 10:"
for (int i = 2; i <= 10; i += 2) {
    print "${i} "
}
println()

Output

Counting from 1 to 5:
1 2 3 4 5

Countdown:
5 4 3 2 1

Even numbers up to 10:
2 4 6 8 10

Nothing surprising here – it works exactly like Java. But honestly, in Groovy you’ll rarely write C-style loops. The for-in loop with ranges is much cleaner for these use cases.

Example 2: For-In with Lists

The for-in loop is Groovy’s enhanced for loop. When you use it with a list, you iterate directly over the elements – no index variable needed.

Example 2: For-In with Lists

// Iterate over a list of strings
def languages = ['Groovy', 'Java', 'Kotlin', 'Scala']

println "Programming Languages:"
for (lang in languages) {
    println "  - ${lang}"
}

// Iterate over a list of numbers
def scores = [95, 82, 78, 91, 88]
def total = 0

for (score in scores) {
    total += score
}

println "\nScores: ${scores}"
println "Total: ${total}"
println "Average: ${total / scores.size()}"

Output

Programming Languages:
  - Groovy
  - Java
  - Kotlin
  - Scala

Scores: [95, 82, 78, 91, 88]
Total: 434
Average: 86.8

Notice how clean this is compared to the C-style approach. No i variable, no size() call, no get(i). You just get each element directly.

Example 3: For-In with Ranges

Ranges are one of Groovy’s best features. A range like 1..5 creates a sequence from 1 to 5 (inclusive), and 1..<5 creates a sequence from 1 to 4 (exclusive upper bound).

Example 3: For-In with Ranges

// Inclusive range (1 to 5)
println "Inclusive range 1..5:"
for (i in 1..5) {
    print "${i} "
}
println()

// Exclusive range (1 to 4, excluding 5)
println "\nExclusive range 1..<5:"
for (i in 1..<5) {
    print "${i} "
}
println()

// Reverse range
println "\nReverse range 5..1:"
for (i in 5..1) {
    print "${i} "
}
println()

// Character range
println "\nCharacter range 'a'..'f':"
for (ch in 'a'..'f') {
    print "${ch} "
}
println()

// Range with step (using step method)
println "\nRange with step of 3:"
for (i in (1..15).step(3)) {
    print "${i} "
}
println()

Output

Inclusive range 1..5:
1 2 3 4 5

Exclusive range 1..<5:
1 2 3 4

Reverse range 5..1:
5 4 3 2 1

Character range 'a'..'f':
a b c d e f

Range with step of 3:
1 4 7 10 13

Ranges are incredibly useful. They replace the C-style for (int i = 1; i <= 5; i++) with the much cleaner for (i in 1..5). And you can even iterate over characters – try doing that elegantly in Java!

Example 4: For-In with Maps

When you loop over a map in Groovy, each iteration gives you a Map.Entry with .key and .value properties. It’s one of those things that just feels right.

Example 4: For-In with Maps

// Iterate over map entries
def capitals = [
    India  : 'New Delhi',
    Japan  : 'Tokyo',
    France : 'Paris',
    Brazil : 'Brasilia'
]

println "World Capitals:"
for (entry in capitals) {
    println "  ${entry.key} → ${entry.value}"
}

// Destructuring map entries
println "\nWith destructuring:"
for (entry in capitals) {
    def (country, capital) = [entry.key, entry.value]
    println "  The capital of ${country} is ${capital}"
}

// Iterate over just keys or values
println "\nJust the countries:"
for (country in capitals.keySet()) {
    print "${country} "
}
println()

println "\nJust the capitals:"
for (capital in capitals.values()) {
    print "${capital} "
}
println()

Output

World Capitals:
  India → New Delhi
  Japan → Tokyo
  France → Paris
  Brazil → Brasilia

With destructuring:
  The capital of India is New Delhi
  The capital of Japan is Tokyo
  The capital of France is Paris
  The capital of Brazil is Brasilia

Just the countries:
India Japan France Brazil

Just the capitals:
New Delhi Tokyo Paris Brasilia

Map iteration in Groovy is smooth. Each entry is a Map.Entry object, so you access .key and .value directly. You can also loop over just keys or just values using keySet() and values().

Example 5: For-In with Strings

Here’s something cool – you can iterate directly over a string’s characters using for-in. No need to call toCharArray() or charAt().

Example 5: For-In with Strings

// Iterate over characters in a string
def word = "Groovy"

println "Characters in '${word}':"
for (ch in word) {
    print "[${ch}] "
}
println()

// Count vowels
def text = "Groovy for loops are awesome"
def vowels = 0

for (ch in text.toLowerCase()) {
    if (ch in ['a', 'e', 'i', 'o', 'u']) {
        vowels++
    }
}
println "\nText: '${text}'"
println "Vowel count: ${vowels}"

// Build a character frequency map
def freq = [:]
for (ch in "mississippi") {
    freq[ch] = (freq[ch] ?: 0) + 1
}
println "\nCharacter frequency of 'mississippi':"
for (entry in freq) {
    println "  '${entry.key}' → ${entry.value}"
}

Output

Characters in 'Groovy':
[G] [r] [o] [o] [v] [y]

Text: 'Groovy for loops are awesome'
Vowel count: 11

Character frequency of 'mississippi':
  'm' → 1
  'i' → 4
  's' → 4
  'p' → 2

String iteration is perfect for character-level processing. The vowel counter and frequency map examples show how you can combine for-in with other Groovy features for concise solutions.

Example 6: For Loop with Step

Sometimes you need to count by 2s, 3s, or some other interval. The C-style loop handles this with i += step, but Groovy ranges with .step() are cleaner.

Example 6: For Loop with Step

// Step with C-style for loop
println "Odd numbers 1-20 (C-style):"
for (int i = 1; i <= 20; i += 2) {
    print "${i} "
}
println()

// Step with range
println "\nMultiples of 5 up to 50:"
for (i in (5..50).step(5)) {
    print "${i} "
}
println()

// Reverse step
println "\nCountdown by 3 from 30:"
for (i in (30..1).step(3)) {
    print "${i} "
}
println()

// Practical: print multiplication table row
def n = 7
println "\n\nMultiplication table for ${n}:"
for (i in 1..10) {
    println "  ${n} x ${i} = ${n * i}"
}

Output

Odd numbers 1-20 (C-style):
1 3 5 7 9 11 13 15 17 19 

Multiples of 5 up to 50:
5 10 15 20 25 30 35 40 45 50 

Countdown by 3 from 30:
30 27 24 21 18 15 12 9 6 3 

Multiplication table for 7:
  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

The .step() method on ranges is a nice Groovy-specific feature. It’s cleaner than i += 2 because your intent is immediately obvious.

Example 7: Enhanced For Loop with Objects

Groovy’s for-in works with any Iterable, including custom objects, sets, queues, and arrays. Here it is in action with different types.

Example 7: Enhanced For Loop with Objects

// With an array
int[] numbers = [10, 20, 30, 40, 50]
println "Array elements:"
for (num in numbers) {
    print "${num} "
}
println()

// With a Set (unordered, no duplicates)
def uniqueColors = ['red', 'blue', 'green', 'red', 'blue'] as Set
println "\nUnique colors:"
for (color in uniqueColors) {
    print "${color} "
}
println()

// With a custom class implementing Iterator
class Countdown implements Iterable<Integer> {
    int start

    Iterator<Integer> iterator() {
        def current = start
        return [
            hasNext: { current > 0 },
            next: { current-- }
        ] as Iterator<Integer>
    }
}

println "\nCustom Countdown from 5:"
for (n in new Countdown(start: 5)) {
    print "${n} "
}
println()

// With an Enum
enum Season { SPRING, SUMMER, AUTUMN, WINTER }

println "\nSeasons:"
for (s in Season.values()) {
    println "  ${s.name().toLowerCase().capitalize()}"
}

Output

Array elements:
10 20 30 40 50

Unique colors:
red blue green

Custom Countdown from 5:
5 4 3 2 1

Seasons:
  Spring
  Summer
  Autumn
  Winter

The beauty of for-in is its universality. Arrays, sets, custom iterables, enums – same syntax, different types. Groovy’s duck typing makes this possible.

Example 8: Nested For Loops

Nested loops are common when working with 2D data structures, matrices, or when you need to compare every pair of items. Here’s how they work in Groovy.

Example 8: Nested For Loops

// Print a simple pattern
println "Triangle pattern:"
for (i in 1..5) {
    def row = ""
    for (j in 1..i) {
        row += "* "
    }
    println row.trim()
}

// 2D list (matrix) traversal
def matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

println "\nMatrix:"
for (row in matrix) {
    def line = ""
    for (cell in row) {
        line += String.format("%3d", cell)
    }
    println line
}

// Find pairs that sum to a target
def nums = [2, 7, 11, 15, 4]
def target = 9
println "\nPairs that sum to ${target}:"

for (i in 0..<nums.size()) {
    for (j in (i + 1)..<nums.size()) {
        if (nums[i] + nums[j] == target) {
            println "  ${nums[i]} + ${nums[j]} = ${target}"
        }
    }
}

Output

Triangle pattern:
*
* *
* * *
* * * *
* * * * *

Matrix:
  1  2  3
  4  5  6
  7  8  9

Pairs that sum to 9:
  2 + 7 = 9

Nested loops are simple. The pair-finding example uses exclusive ranges (0..<nums.size()) to avoid index out-of-bounds errors – the ..< operator excludes the upper bound.

Example 9: Labeled Break and Continue

Groovy supports labeled loops, just like Java. Labels let you break out of or continue an outer loop from inside an inner loop. This is one of the key advantages of for loops over each().

Example 9: Labeled Break and Continue

// Simple break - find first negative number
def values = [5, 12, -3, 8, -7, 15]
println "Finding first negative number:"
for (v in values) {
    if (v < 0) {
        println "  Found: ${v}"
        break
    }
    println "  Checked: ${v} (positive)"
}

// Simple continue - skip even numbers
println "\nOdd numbers only:"
for (i in 1..10) {
    if (i % 2 == 0) continue
    print "${i} "
}
println()

// Labeled break - exit outer loop from inner loop
println "\nLabeled break (find first duplicate):"
def items = ['apple', 'banana', 'cherry', 'banana', 'date']

outer:
for (i in 0..<items.size()) {
    for (j in (i + 1)..<items.size()) {
        if (items[i] == items[j]) {
            println "  Duplicate found: '${items[i]}' at index ${i} and ${j}"
            break outer
        }
    }
}

// Labeled continue - skip specific outer iterations
println "\nLabeled continue (skip rows with zeros):"
def data = [
    [1, 2, 3],
    [4, 0, 6],
    [7, 8, 9]
]

outer:
for (row in data) {
    for (cell in row) {
        if (cell == 0) {
            println "  Skipping row with zero: ${row}"
            continue outer
        }
    }
    println "  Processing row: ${row} → sum = ${row.sum()}"
}

Output

Finding first negative number:
  Checked: 5 (positive)
  Checked: 12 (positive)
  Found: -3

Odd numbers only:
1 3 5 7 9

Labeled break (find first duplicate):
  Duplicate found: 'banana' at index 1 and 3

Labeled continue (skip rows with zeros):
  Processing row: [1, 2, 3] → sum = 6
  Skipping row with zero: [4, 0, 6]
  Processing row: [7, 8, 9] → sum = 24

This is where for loops really shine over each(). You cannot use break or continue inside an each() closure – they only work inside traditional loops. If you need to exit early, use a for loop.

Example 10: For vs Each vs Times Comparison

Let’s put all three looping approaches side by side so you can see the differences clearly. Each approach has its strengths, and knowing when to pick which one is key to writing idiomatic Groovy.

Example 10: For vs Each vs Times

def fruits = ['apple', 'banana', 'cherry', 'date', 'elderberry']

// Approach 1: Classic for-in loop
println "=== for-in loop ==="
for (fruit in fruits) {
    println "  ${fruit.toUpperCase()}"
}

// Approach 2: each() closure
println "\n=== each() closure ==="
fruits.each { fruit ->
    println "  ${fruit.toUpperCase()}"
}

// Approach 3: eachWithIndex (when you need the index)
println "\n=== eachWithIndex() ==="
fruits.eachWithIndex { fruit, idx ->
    println "  ${idx + 1}. ${fruit}"
}

// Approach 4: times() for simple counting
println "\n=== 3.times() ==="
3.times { i ->
    println "  Iteration ${i}"
}

// Approach 5: for-in with index (manual)
println "\n=== for-in with manual index ==="
def idx = 0
for (fruit in fruits) {
    println "  [${idx}] ${fruit}"
    idx++
}

// Key difference: break works in for, not in each
println "\n=== break in for loop ==="
for (fruit in fruits) {
    if (fruit.startsWith('c')) {
        println "  Stopped at: ${fruit}"
        break
    }
    println "  Processing: ${fruit}"
}

Output

=== for-in loop ===
  APPLE
  BANANA
  CHERRY
  DATE
  ELDERBERRY

=== each() closure ===
  APPLE
  BANANA
  CHERRY
  DATE
  ELDERBERRY

=== eachWithIndex() ===
  1. apple
  2. banana
  3. cherry
  4. date
  5. elderberry

=== 3.times() ===
  Iteration 0
  Iteration 1
  Iteration 2

=== for-in with manual index ===
  [0] apple
  [1] banana
  [2] cherry
  [3] date
  [4] elderberry

=== break in for loop ===
  Processing: apple
  Processing: banana
  Stopped at: cherry

Here’s a quick rule of thumb: use each() for simple iteration, times() for repeat-N scenarios, and for-in when you need break/continue or more complex control flow.

Example 11: Looping with Index

Sometimes you need both the element and its position. Groovy gives you multiple ways to loop with an index.

Example 11: Looping with Index

def colors = ['red', 'green', 'blue', 'yellow', 'purple']

// Method 1: C-style for loop
println "Method 1: C-style index"
for (int i = 0; i < colors.size(); i++) {
    println "  colors[${i}] = ${colors[i]}"
}

// Method 2: for-in with range as index
println "\nMethod 2: Range-based index"
for (i in 0..<colors.size()) {
    println "  colors[${i}] = ${colors[i]}"
}

// Method 3: indexed() method (Groovy 2.4+)
println "\nMethod 3: indexed() with for-in"
for (entry in colors.indexed()) {
    println "  colors[${entry.key}] = ${entry.value}"
}

// Practical: Find the index of the maximum value
def scores = [72, 95, 88, 61, 95, 83]
def maxScore = scores.max()
def maxIndices = []

for (i in 0..<scores.size()) {
    if (scores[i] == maxScore) {
        maxIndices << i
    }
}
println "\nMax score ${maxScore} found at index: ${maxIndices}"

Output

Method 1: C-style index
  colors[0] = red
  colors[1] = green
  colors[2] = blue
  colors[3] = yellow
  colors[4] = purple

Method 2: Range-based index
  colors[0] = red
  colors[1] = green
  colors[2] = blue
  colors[3] = yellow
  colors[4] = purple

Method 3: indexed() with for-in
  colors[0] = red
  colors[1] = green
  colors[2] = blue
  colors[3] = yellow
  colors[4] = purple

Max score 95 found at index: [1, 4]

The indexed() method returns a map of index-to-value pairs, which is elegant for for-in loops. For closure-based approaches, eachWithIndex() is the way to go.

Example 12: Infinite Loops and Guarded Exits

While infinite loops are generally bad, sometimes they’re exactly what you need – like reading from a stream until it’s exhausted, or implementing a retry mechanism. Groovy handles these just like Java.

Example 12: Infinite Loops and Guarded Exits

// Simulate a retry mechanism
def attempts = 0
def success = false

println "Simulating retry logic:"
for (;;) {  // infinite loop
    attempts++
    println "  Attempt ${attempts}..."

    // Simulate success on 3rd attempt
    if (attempts == 3) {
        success = true
        println "  Success!"
        break
    }
    println "  Failed, retrying..."
}
println "Total attempts: ${attempts}"

// Practical: Simple number guessing game simulation
println "\nGuessing game simulation:"
def secret = 42
def guesses = [10, 25, 50, 42, 60]
def guessIndex = 0

for (;;) {
    def guess = guesses[guessIndex++]
    print "  Guess: ${guess} → "

    if (guess == secret) {
        println "Correct! Found in ${guessIndex} guesses."
        break
    } else if (guess < secret) {
        println "Too low!"
    } else {
        println "Too high!"
    }
}

// While-style for loop
println "\nCollatz sequence starting from 12:"
def num = 12
print "${num}"
for (; num != 1;) {
    num = (num % 2 == 0) ? num / 2 : num * 3 + 1
    print " → ${num}"
}
println()

Output

Simulating retry logic:
  Attempt 1...
  Failed, retrying...
  Attempt 2...
  Failed, retrying...
  Attempt 3...
  Success!
Total attempts: 3

Guessing game simulation:
  Guess: 10 → Too low!
  Guess: 25 → Too low!
  Guess: 50 → Too high!
  Guess: 42 → Correct! Found in 4 guesses.

Collatz sequence starting from 12:
12 → 6 → 3 → 10 → 5 → 16 → 8 → 4 → 2 → 1

The for (;;) syntax creates an infinite loop – always make sure you have a break condition or you’ll hang your program. The Collatz sequence example shows how you can use for in a while-loop style by omitting the initializer and update parts.

Example 13: Real-World – Processing Collections

Let’s bring everything together with a real-world scenario: processing a list of employee records with filtering, transformation, and aggregation.

Example 13: Real-World Collection Processing

// Employee data
def employees = [
    [name: 'Alice',   dept: 'Engineering', salary: 95000],
    [name: 'Bob',     dept: 'Marketing',   salary: 72000],
    [name: 'Charlie', dept: 'Engineering', salary: 88000],
    [name: 'Diana',   dept: 'HR',          salary: 68000],
    [name: 'Eve',     dept: 'Engineering', salary: 110000],
    [name: 'Frank',   dept: 'Marketing',   salary: 65000],
]

// Calculate department-wise statistics
def deptStats = [:]

for (emp in employees) {
    def dept = emp.dept
    if (!deptStats.containsKey(dept)) {
        deptStats[dept] = [count: 0, total: 0, employees: []]
    }
    deptStats[dept].count++
    deptStats[dept].total += emp.salary
    deptStats[dept].employees << emp.name
}

println "Department Statistics:"
println "-" * 50
for (entry in deptStats) {
    def dept = entry.key
    def stats = entry.value
    def avg = stats.total / stats.count
    println "  ${dept}:"
    println "    Employees: ${stats.employees.join(', ')}"
    println "    Count: ${stats.count}"
    println "    Avg Salary: \$${String.format('%,.0f', avg)}"
    println()
}

// Find highest paid in each department
println "Highest Paid Per Department:"
for (entry in deptStats) {
    def dept = entry.key
    def highest = null

    for (emp in employees) {
        if (emp.dept == dept) {
            if (highest == null || emp.salary > highest.salary) {
                highest = emp
            }
        }
    }
    println "  ${dept}: ${highest.name} (\$${String.format('%,d', highest.salary)})"
}

Output

Department Statistics:
--------------------------------------------------
  Engineering:
    Employees: Alice, Charlie, Eve
    Count: 3
    Avg Salary: $97,667

  Marketing:
    Employees: Bob, Frank
    Count: 2
    Avg Salary: $68,500

  HR:
    Employees: Diana
    Count: 1
    Avg Salary: $68,000

Highest Paid Per Department:
  Engineering: Eve ($110,000)
  Marketing: Bob ($72,000)
  HR: Diana ($68,000)

This example shows for loops doing real work – grouping data, computing aggregates, and finding maximums. In production code you might use groupBy() and collectEntries() for these tasks, but understanding the for-loop approach gives you a solid foundation.

Example 14: Building Strings with For Loops

Building strings iteratively is a common task. Here are different patterns for constructing output with for loops.

Example 14: Building Strings

// Build a CSV row
def headers = ['Name', 'Age', 'City']
def values = ['Alice', 30, 'New York']
def csv = new StringBuilder()

for (i in 0..<headers.size()) {
    if (i > 0) csv.append(',')
    csv.append(values[i])
}
println "CSV: ${csv}"

// Build an HTML list
def items = ['Learn Groovy', 'Build a project', 'Deploy to production']
def html = new StringBuilder("<ul>\n")

for (item in items) {
    html.append("  <li>${item}</li>\n")
}
html.append("</ul>")
println "\nHTML:\n${html}"

// Build a formatted table
def products = [
    [name: 'Laptop',  price: 999.99],
    [name: 'Mouse',   price: 29.99],
    [name: 'Keyboard', price: 79.99],
]

def table = new StringBuilder()
table.append(String.format("%-12s %10s%n", "Product", "Price"))
table.append("-" * 23 + "\n")

for (product in products) {
    table.append(String.format("%-12s %10s%n", product.name, "\$${product.price}"))
}

def total = 0
for (product in products) {
    total += product.price
}
table.append("-" * 23 + "\n")
table.append(String.format("%-12s %10s", "TOTAL", "\$${total}"))

println "\n${table}"

Output

CSV: Alice,30,New York

HTML:

  Learn Groovy
  Build a project
  Deploy to production

Product           Price
-----------------------
Laptop          $999.99
Mouse            $29.99
Keyboard         $79.99
-----------------------
TOTAL          $1109.97

When building strings inside a loop, always use StringBuilder instead of string concatenation with +. Concatenation creates a new String object every iteration, which gets expensive fast. Alternatively, use collect() and join() for a more Groovy-idiomatic approach.

Edge Cases and Best Practices

Best Practices Summary

DO:

  • Use for (item in collection) instead of C-style loops when possible
  • Use ranges (1..n) for numeric iteration – they’re cleaner and less error-prone
  • Use exclusive ranges (0..<list.size()) for index-based iteration to avoid off-by-one errors
  • Use for loops when you need break or continue
  • Use StringBuilder for string concatenation inside loops

DON’T:

  • Modify a collection while iterating over it with for-in – it throws ConcurrentModificationException
  • Use C-style for loops when a simple range or each() would do
  • Forget that break and continue don’t work inside each() closures
  • Write infinite loops without a clear exit condition

Edge Case: Modifying During Iteration

// WRONG: Modifying list during for-in loop
def numbers = [1, 2, 3, 4, 5]
try {
    for (n in numbers) {
        if (n == 3) numbers.remove((Object) n)
    }
} catch (ConcurrentModificationException e) {
    println "ConcurrentModificationException caught!"
}

// CORRECT: Use removeAll or iterate over a copy
def numbers2 = [1, 2, 3, 4, 5]
def copy = new ArrayList(numbers2)
for (n in copy) {
    if (n % 2 == 0) numbers2.remove((Object) n)
}
println "After removing evens: ${numbers2}"

// BEST: Use removeAll with a condition
def numbers3 = [1, 2, 3, 4, 5]
numbers3.removeAll { it % 2 == 0 }
println "After removeAll evens: ${numbers3}"

Output

ConcurrentModificationException caught!
After removing evens: [1, 3, 5]
After removeAll evens: [1, 3, 5]

The ConcurrentModificationException is one of the most common loop-related errors. Never add or remove elements from a collection while iterating over it. Either iterate over a copy, or use removeAll()/retainAll().

Performance Considerations

For most applications, the performance difference between loop types is negligible. But if you’re processing millions of items, here are some things to keep in mind:

Performance Comparison

def size = 1_000_000

// Measure C-style for loop
def start1 = System.nanoTime()
def sum1 = 0L
for (int i = 0; i < size; i++) {
    sum1 += i
}
def time1 = (System.nanoTime() - start1) / 1_000_000

// Measure for-in with range
def start2 = System.nanoTime()
def sum2 = 0L
for (i in 0..<size) {
    sum2 += i
}
def time2 = (System.nanoTime() - start2) / 1_000_000

// Measure each closure
def start3 = System.nanoTime()
def sum3 = 0L
(0..<size).each { sum3 += it }
def time3 = (System.nanoTime() - start3) / 1_000_000

println "Results (all sums equal: ${sum1 == sum2 && sum2 == sum3}):"
println "  C-style for:   ${time1}ms"
println "  for-in range:  ${time2}ms"
println "  each closure:  ${time3}ms"

Output

Results (all sums equal: true):
  C-style for:   12ms
  for-in range:  45ms
  each closure:  62ms

Note: These numbers vary by JVM, hardware, and warmup state. The C-style for loop is typically fastest because it avoids iterator and closure overhead. But unless you’re in a hot loop processing millions of items, readability matters more than a few milliseconds. Choose the loop style that makes your code clearest.

Common Pitfalls

Pitfall 1: Break Inside each() Doesn’t Work

Pitfall 1: break in each()

// WRONG: break inside each() causes compilation error
// [1, 2, 3, 4, 5].each {
//     if (it == 3) break  // ERROR!
//     println it
// }

// CORRECT: Use a for loop when you need break
println "Using for loop with break:"
for (n in [1, 2, 3, 4, 5]) {
    if (n == 3) break
    println "  ${n}"
}

// ALTERNATIVE: Use find() to stop at first match
println "\nUsing find() instead:"
def found = [1, 2, 3, 4, 5].find { it == 3 }
println "  Found: ${found}"

Output

Using for loop with break:
  1
  2

Using find() instead:
  Found: 3

This is the most common pitfall for Groovy beginners. The each() method accepts a closure, and break/continue are language-level keywords that only work inside loops – not closures. If you need early termination, use a for loop or methods like find(), any(), or takeWhile().

Pitfall 2: Off-By-One with Ranges

Pitfall 2: Off-By-One Errors

def list = ['a', 'b', 'c', 'd', 'e']

// WRONG: Inclusive range goes out of bounds
// for (i in 0..list.size()) {  // 0..5 includes index 5 → IndexOutOfBoundsException
//     println list[i]
// }

// CORRECT: Use exclusive range
println "Correct iteration with exclusive range:"
for (i in 0..<list.size()) {
    print "${list[i]} "
}
println()

// ALSO CORRECT: Just use for-in directly
println "Even better - iterate directly:"
for (item in list) {
    print "${item} "
}
println()

Output

Correct iteration with exclusive range:
a b c d e

Even better - iterate directly:
a b c d e

Remember: 0..list.size() is inclusive and includes the size itself (which is out of bounds). Use 0..<list.size() with the exclusive operator, or better yet, iterate directly with for (item in list).

Pitfall 3: Variable Scope in For Loops

Pitfall 3: Variable Scope

// The loop variable is scoped to the loop body
for (i in 1..3) {
    def message = "Iteration ${i}"
    println message
}
// println message  // ERROR: message is not defined here

// In Groovy 5, loop variables are properly scoped (like Java)
for (x in 1..3) { }
// println "x after loop: ${x}"  // ERROR in Groovy 5: x is not defined here
// (In Groovy 4 and earlier, x would still be accessible)

Output

Iteration 1
Iteration 2
Iteration 3
x after loop: 3

In Groovy scripts (not classes), the loop variable from for-in leaks into the surrounding scope. This is because scripts use a binding object. Inside a class method, the variable is properly scoped. Be aware of this behavior to avoid unexpected bugs.

Conclusion

We’ve covered every Groovy for loop variation you’ll encounter – from the classic C-style loop to the idiomatic for-in with ranges, lists, maps, and strings. We also looked at how for loops compare to each() and times(), and when to pick each approach.

The bottom line is this: use for (item in collection) for most iteration tasks. Use C-style for loops when you need precise index control. And use each() when you want functional-style code and don’t need break/continue.

Next up in this series, we’ll cover Groovy’s each() method – the closure-based iteration approach that most Groovy developers prefer for everyday looping.

Summary

  • Groovy supports C-style for loops and enhanced for-in loops
  • for (item in collection) is the idiomatic Groovy way to iterate
  • Ranges (1..10, 1..<10) replace verbose C-style counting loops
  • break and continue work in for loops but NOT in each() closures
  • Never modify a collection while iterating over it – use a copy or removeAll()

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 Each Loop – Closure-Based Iteration

Frequently Asked Questions

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

The for loop is a language-level control structure that supports break and continue. The each() method is a GDK method that accepts a closure – it’s more concise but doesn’t support break or continue. Use for when you need early exit, and each() for simple iteration where you process every element.

How do I use a for loop with an index in Groovy?

You have several options: use a C-style loop (for (int i = 0; i < list.size(); i++)), use a range (for (i in 0.. }.

Can I use break inside a Groovy each() closure?

No. The break and continue keywords only work inside language-level loops (for, while, do-while). Since each() uses a closure, break will cause a compilation error. Use a for loop if you need break, or use find(), any(), or takeWhile() as functional alternatives.

What is a Groovy range and how do I use it in a for loop?

A Groovy range is a sequence of values with a start and end. Use 1..5 for inclusive (1,2,3,4,5) or 1..<5 for exclusive (1,2,3,4). In a for loop: for (i in 1..10) { println i }. Ranges work with integers, characters (‘a’..’z’), and any Comparable that implements next()/previous().

Which for loop is fastest in Groovy?

The C-style for loop (for (int i = 0; i < n; i++)) is typically fastest because it avoids iterator and closure overhead. The for-in loop is slightly slower due to iterator creation, and each() is slowest due to closure invocation. However, the difference is negligible for most applications – choose readability over micro-optimization.

Previous in Series: Groovy Collections Overview – Lists, Maps, and Sets

Next in Series: Groovy Each Loop – Closure-Based Iteration

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 *