The Groovy spread operator (*.) lets you call methods and access properties on every element in a collection. 10+ tested examples with output on Groovy 5.x.
“Why write a loop when a single operator can do the job? The spread operator is Groovy’s answer to ‘apply this to everything.'”
Venkat Subramaniam, Programming Groovy 2
Last Updated: March 2026 | Tested on: Groovy 5.x, Java 17+ | Difficulty: Beginner to Intermediate | Reading Time: 15 minutes
How many times have you written a loop or a collect closure just to call the same method on every element in a list? In Java, that is the only way. In Groovy, you have the Groovy spread operator (*.), which does exactly that in a single, elegant expression.
The spread operator lets you invoke a method or access a property on every element in a collection, returning a new list of results. Instead of names.collect { it.toUpperCase() }, you simply write names*.toUpperCase(). Same result, less code, more expressive. It is one of those features that makes Groovy code feel like it reads your mind.
In this post, we will walk through 10+ tested examples of the spread operator applied to strings, objects, nested properties, method arguments, and real-world scenarios. If you are also working with null values in your collections, make sure to check out the Groovy Elvis operator and the Groovy safe navigation operator for the complete null-handling toolkit.
Table of Contents
What is the Spread Operator?
The Groovy spread operator (*.) is also called the spread-dot operator. It calls a method or accesses a property on each element in a collection and returns a list of results. Think of it as a shortcut for collect when you just need to call a single method or access a single property.
According to the official Groovy operators documentation, the spread operator is part of Groovy’s rich set of collection manipulation tools that make working with lists and arrays concise and readable.
Groovy actually has two related spread features:
- Spread-dot operator (
*.): Calls a method or accesses a property on each element —list*.method() - Spread operator (
*): Unpacks a collection into individual arguments —method(*list)
We will cover both in this post, with the spread-dot operator being the primary focus since it is used far more frequently.
Spread Operator Syntax
Let us look at the syntax with a simple comparison to the traditional approach.
Spread Operator Syntax
def names = ["alice", "bob", "charlie"]
// Traditional: collect with closure
def upper1 = names.collect { it.toUpperCase() }
// Spread-dot: same result, shorter syntax
def upper2 = names*.toUpperCase()
// Both produce: [ALICE, BOB, CHARLIE]
assert upper1 == upper2
// Property access works too
class Person { String name; int age }
def people = [new Person(name: "Alice", age: 30), new Person(name: "Bob", age: 25)]
// Traditional
def names1 = people.collect { it.name }
// Spread-dot
def names2 = people*.name
// Both produce: [Alice, Bob]
assert names1 == names2
The pattern is collection*.methodOrProperty. Groovy iterates through the collection, calls the method or accesses the property on each element, and collects the results into a new list.
10+ Practical Examples
Let us work through real examples you can copy and run. Every example has been tested on Groovy 5.x with actual output shown.
Example 1: Spread-Dot on String Methods
The most common use of the spread operator: calling string methods on every element in a list.
Example 1: Spread-Dot on Strings
def words = ["hello", "world", "groovy"]
// Call toUpperCase() on every element
def upper = words*.toUpperCase()
println "Upper: ${upper}"
// Call length() on every element
def lengths = words*.length()
println "Lengths: ${lengths}"
// Call capitalize() on every element
def capitalized = words*.capitalize()
println "Capitalized: ${capitalized}"
// Call reverse() on every element
def reversed = words*.reverse()
println "Reversed: ${reversed}"
// Call trim() on padded strings
def padded = [" hello ", " world ", " groovy"]
def trimmed = padded*.trim()
println "Trimmed: ${trimmed}"
Output
Upper: [HELLO, WORLD, GROOVY] Lengths: [5, 5, 6] Capitalized: [Hello, World, Groovy] Reversed: [olleh, dlrow, yvoorg] Trimmed: [hello, world, groovy]
Any method that the elements support can be called with the spread-dot operator. It works the same as collect { it.method() } but is shorter and often more readable for single method calls.
Example 2: Spread-Dot for Property Access
The spread operator excels at extracting a single property from a list of objects. This is the pattern you will use the most in Groovy.
Example 2: Spread-Dot for Properties
class Employee {
String name
String department
int salary
}
def employees = [
new Employee(name: "Alice", department: "Engineering", salary: 95000),
new Employee(name: "Bob", department: "Marketing", salary: 75000),
new Employee(name: "Charlie", department: "Engineering", salary: 105000),
new Employee(name: "Diana", department: "Marketing", salary: 80000)
]
// Extract single property from all objects
def names = employees*.name
println "Names: ${names}"
def departments = employees*.department
println "Departments: ${departments}"
def salaries = employees*.salary
println "Salaries: ${salaries}"
// Use extracted data for calculations
println "\nTotal payroll: ${salaries.sum()}"
println "Average salary: ${salaries.sum() / salaries.size()}"
println "Unique departments: ${departments.unique()}"
println "Highest salary: ${salaries.max()}"
Output
Names: [Alice, Bob, Charlie, Diana] Departments: [Engineering, Marketing, Engineering, Marketing] Salaries: [95000, 75000, 105000, 80000] Total payroll: 355000 Average salary: 88750 Unique departments: [Engineering, Marketing] Highest salary: 105000
The employees*.name pattern is the idiomatic Groovy way to extract a column of data from a list of objects. You will see this everywhere in Grails controllers, services, and test code.
Example 3: Spread-Dot with Maps
The spread operator works with maps too, operating on the map entries. You can also spread-dot into map values within a list of maps.
Example 3: Spread-Dot with Maps
// List of maps (common when working with JSON data)
def users = [
[name: "Alice", age: 30, city: "London"],
[name: "Bob", age: 25, city: "Paris"],
[name: "Charlie", age: 35, city: "Tokyo"]
]
// Extract values by key using spread-dot with property-style access
def names = users*.name
println "Names: ${names}"
def cities = users*.city
println "Cities: ${cities}"
def ages = users*.age
println "Ages: ${ages}"
// Spread-dot with map's keySet and values
def singleMap = [a: 1, b: 2, c: 3]
def keys = singleMap.keySet()*.toUpperCase()
println "\nUppercase keys: ${keys}"
def values = singleMap.values()*.multiply(10)
println "Values x10: ${values}"
// Spread-dot on map entries
def entries = singleMap.collect { k, v -> "${k}=${v}" }
println "Entries: ${entries}"
Output
Names: [Alice, Bob, Charlie] Cities: [London, Paris, Tokyo] Ages: [30, 25, 35] Uppercase keys: [A, B, C] Values x10: [10, 20, 30] Entries: [a=1, b=2, c=3]
Working with a list of maps is extremely common in Groovy, especially when dealing with parsed JSON responses. The spread-dot operator makes extracting a specific field from all records a one-liner.
Example 4: Spread-Dot with Method Arguments
You can pass arguments to the spread-dot method call, just like a normal method invocation.
Example 4: Spread-Dot with Method Arguments
def strings = ["Hello World", "Groovy Rocks", "Spread Operator"]
// Call replaceAll() on every string
def dashed = strings*.replaceAll(' ', '-')
println "Dashed: ${dashed}"
// Call substring() on every string
def first5 = strings*.take(5)
println "First 5 chars: ${first5}"
// Call padRight() on every string
def padded = ["cat", "elephant", "dog"]*.padRight(10, '.')
println "Padded: ${padded}"
// Call split() on every string
def words = ["a,b,c", "d,e,f", "g,h,i"]*.split(',')
println "Split: ${words}"
// Numbers: multiply each
def numbers = [1, 2, 3, 4, 5]
def doubled = numbers*.multiply(2)
println "\nDoubled: ${doubled}"
// Call plus() on each
def incremented = numbers*.plus(10)
println "Plus 10: ${incremented}"
Output
Dashed: [Hello-World, Groovy-Rocks, Spread-Operator] First 5 chars: [Hello, Groov, Sprea] Padded: [cat......., elephant.., dog.......] Split: [[a, b, c], [d, e, f], [g, h, i]] Doubled: [2, 4, 6, 8, 10] Plus 10: [11, 12, 13, 14, 15]
Any method with any number of arguments works with the spread-dot operator. The arguments are passed identically to each element’s method call.
Example 5: Spread Operator for Unpacking Arguments (*)
Groovy also has the spread operator (*) for unpacking a collection into individual method arguments. This is different from the spread-dot (*.) but equally useful.
Example 5: Spread Operator for Argument Unpacking
// Define a method that takes individual parameters
def greet(String first, String last, String greeting) {
return "${greeting}, ${first} ${last}!"
}
// Call with a list, spreading it into arguments
def args = ["Alex", "Johnson", "Hello"]
println greet(*args)
// Mixing spread with regular arguments
def nameArgs = ["Alice", "Smith"]
println greet(*nameArgs, "Hi")
// Spread into list construction
def first = [1, 2, 3]
def second = [4, 5, 6]
def combined = [*first, *second]
println "\nCombined: ${combined}"
// Spread into list with extra elements
def extended = [0, *first, 99, *second, 100]
println "Extended: ${extended}"
// Spread into map construction
def defaults = [host: "localhost", port: 8080]
def overrides = [port: 3000, debug: true]
def config = [*:defaults, *:overrides]
println "\nConfig: ${config}"
// The later spread wins for duplicate keys
def base = [a: 1, b: 2, c: 3]
def patch = [b: 20, d: 40]
def merged = [*:base, *:patch]
println "Merged: ${merged}"
Output
Hello, Alex Johnson! Hi, Alice Smith! Combined: [1, 2, 3, 4, 5, 6] Extended: [0, 1, 2, 3, 99, 4, 5, 6, 100] Config: [host:localhost, port:3000, debug:true] Merged: [a:1, b:20, c:3, d:40]
The map spread ([*:map1, *:map2]) is particularly useful for merging configuration maps. The last spread wins for duplicate keys, which is exactly what you want for an “overrides” pattern. This is a common idiom in Gradle build scripts and Grails configuration.
Example 6: Spread-Dot with Null Elements
What happens when your list contains null elements? The spread-dot operator handles this differently than you might expect.
Example 6: Spread-Dot with Null Elements
// List with null elements
def names = ["Alice", null, "Charlie", null, "Eve"]
// Spread-dot on list with nulls -- NullPointerException!
// def upper = names*.toUpperCase() // BOOM! NPE on null elements
// Solution 1: Filter nulls first
def safeUpper = names.findAll { it != null }*.toUpperCase()
println "Filtered: ${safeUpper}"
// Solution 2: Use collect with safe navigation
def withNulls = names.collect { it?.toUpperCase() }
println "With nulls: ${withNulls}"
// Spread-dot on a null list itself returns null
List nullList = null
def result = nullList*.toUpperCase()
println "\nNull list spread: ${result}"
// Property access on list with null elements works differently
class Person { String name }
def people = [new Person(name: "Alice"), null, new Person(name: "Charlie")]
// This also throws NPE with null elements
// def personNames = people*.name // NPE!
// Safe approach
def personNames = people.findAll()*.name
println "Person names: ${personNames}"
// Or with collect
def allNames = people.collect { it?.name }
println "All names: ${allNames}"
Output
Filtered: [ALICE, CHARLIE, EVE] With nulls: [ALICE, null, CHARLIE, null, EVE] Null list spread: null Person names: [Alice, Charlie] All names: [Alice, null, Charlie]
This is the most important gotcha with the Groovy spread operator: it does not handle null elements inside the list. If any element is null and you call a method on it, you get a NullPointerException. Either filter nulls first with findAll() or use collect with the safe navigation operator.
Example 7: Spread-Dot for Nested Property Access
You can chain the spread-dot operator to access nested properties, though the syntax needs a bit of care.
Example 7: Nested Spread-Dot
class Address {
String city
String country
}
class Person {
String name
Address address
}
def people = [
new Person(name: "Alice", address: new Address(city: "London", country: "UK")),
new Person(name: "Bob", address: new Address(city: "Paris", country: "France")),
new Person(name: "Charlie", address: new Address(city: "Tokyo", country: "Japan"))
]
// Extract nested property: first spread to get addresses, then spread to get cities
def cities = people*.address*.city
println "Cities: ${cities}"
def countries = people*.address*.country
println "Countries: ${countries}"
// You can also do it in one shot with collect
def citiesAlt = people.collect { it.address.city }
println "Cities (collect): ${citiesAlt}"
// Spread-dot with transformation
def upperCities = people*.address*.city*.toUpperCase()
println "Upper cities: ${upperCities}"
// Extract and process
def cityLengths = people*.address*.city*.length()
println "City name lengths: ${cityLengths}"
Output
Cities: [London, Paris, Tokyo] Countries: [UK, France, Japan] Cities (collect): [London, Paris, Tokyo] Upper cities: [LONDON, PARIS, TOKYO] City name lengths: [6, 5, 5]
Chaining *.address*.city first extracts all addresses into a list, then extracts all cities from that list. Each *. operates on the result of the previous one. This is incredibly powerful for drilling into nested data structures.
Example 8: Spread-Dot with Arrays and Ranges
The spread operator is not limited to lists. It works on arrays, ranges, and any iterable.
Example 8: Spread-Dot with Arrays and Ranges
// Spread on an array
String[] nameArray = ["alice", "bob", "charlie"]
def upperArray = nameArray*.toUpperCase()
println "Array spread: ${upperArray}"
println "Result type: ${upperArray.getClass().simpleName}"
// Spread on a range
def range = (1..5)
def doubled = range*.multiply(2)
println "\nRange doubled: ${doubled}"
// Spread on a set
def uniqueWords = ["hello", "world", "hello", "groovy"] as Set
def upperSet = uniqueWords*.toUpperCase()
println "Set spread: ${upperSet}"
// Spread on characters of a string (string as a list of chars)
def chars = "hello".toList()*.toUpperCase()
println "Char spread: ${chars}"
// Spread on a range of strings
def letters = ('a'..'f')*.toUpperCase()
println "Letter range: ${letters}"
// Spread on nested arrays (flattening first)
def nested = [[1, 2], [3, 4], [5, 6]]
def sizes = nested*.size()
println "\nNested sizes: ${sizes}"
def flattened = nested.flatten()*.multiply(10)
println "Flattened x10: ${flattened}"
Output
Array spread: [ALICE, BOB, CHARLIE] Result type: ArrayList Range doubled: [2, 4, 6, 8, 10] Set spread: [HELLO, WORLD, GROOVY] Char spread: [H, E, L, L, O] Letter range: [A, B, C, D, E, F] Nested sizes: [2, 2, 2] Flattened x10: [10, 20, 30, 40, 50, 60]
Note that the spread-dot operator always returns an ArrayList, regardless of the input type. Even if you start with a Set or an array, the result is always a list.
Example 9: Spread-Dot Chained with Collection Operations
The spread-dot operator chains beautifully with other Groovy collection methods like sort, unique, findAll, and more.
Example 9: Spread-Dot Chained with Collection Ops
class Product {
String name
String category
double price
}
def products = [
new Product(name: "Laptop", category: "Electronics", price: 999.99),
new Product(name: "Mouse", category: "Electronics", price: 29.99),
new Product(name: "Desk", category: "Furniture", price: 249.99),
new Product(name: "Chair", category: "Furniture", price: 399.99),
new Product(name: "Monitor", category: "Electronics", price: 549.99)
]
// Spread + sort
def sortedNames = products*.name.sort()
println "Sorted names: ${sortedNames}"
// Spread + unique
def categories = products*.category.unique()
println "Categories: ${categories}"
// Spread + sum
def totalValue = products*.price.sum()
println "Total value: ${totalValue}"
// Filter first, then spread
def electronics = products.findAll { it.category == "Electronics" }
def electronicNames = electronics*.name
def electronicPrices = electronics*.price
println "\nElectronics: ${electronicNames}"
println "Electronics total: ${electronicPrices.sum()}"
// Spread + join for display
def priceList = products.collect { "${it.name}: \$${it.price}" }.join(' | ')
println "\nPrice list: ${priceList}"
// Sort by price, then get names
def byPrice = products.sort { it.price }*.name
println "Cheapest first: ${byPrice}"
Output
Sorted names: [Chair, Desk, Laptop, Monitor, Mouse] Categories: [Electronics, Furniture] Total value: 2229.95 Electronics: [Laptop, Mouse, Monitor] Electronics total: 1579.97 Price list: Laptop: $999.99 | Mouse: $29.99 | Desk: $249.99 | Chair: $399.99 | Monitor: $549.99 Cheapest first: [Mouse, Desk, Chair, Monitor, Laptop]
The products*.name.sort() pattern is clean and readable: “get all product names, then sort them.” Each operation flows naturally into the next.
Example 10: Spread Operator for Constructor Calls and Builder Patterns
The spread operator (*) is useful for unpacking arguments into constructors and builder method calls.
Example 10: Spread in Constructors and Builders
// Spread into method calls
def sum(int a, int b, int c) { return a + b + c }
def numbers = [10, 20, 30]
println "Sum: ${sum(*numbers)}"
// Spread with varargs
def log(String level, String... messages) {
messages.each { println "[${level}] ${it}" }
}
def errors = ["File not found", "Permission denied"]
log("ERROR", *errors)
// Spread for list construction from multiple sources
def admins = ["Alice", "Bob"]
def users = ["Charlie", "Diana"]
def guests = ["Eve"]
def allPeople = [*admins, *users, *guests]
println "\nAll people: ${allPeople}"
// Spread for map merging (configuration layering)
def defaults = [timeout: 30, retries: 3, verbose: false]
def envConfig = [timeout: 60, verbose: true]
def cliArgs = [retries: 5]
def finalConfig = [*:defaults, *:envConfig, *:cliArgs]
println "Final config: ${finalConfig}"
// Building dynamic method arguments
def formatDate(int year, int month, int day) {
return "${year}-${String.format('%02d', month)}-${String.format('%02d', day)}"
}
def dateParts = [2026, 3, 8]
println "\nDate: ${formatDate(*dateParts)}"
Output
Sum: 60 [ERROR] File not found [ERROR] Permission denied All people: [Alice, Bob, Charlie, Diana, Eve] Final config: [timeout:60, retries:5, verbose:true] Date: 2026-03-08
The argument unpacking spread (*list) and the map spread ([*:map]) are distinct from the spread-dot (*.), but they are all part of the same family of spread operations in Groovy.
Example 11: Spread-Dot with GStrings and toString()
Using spread-dot to convert objects to strings and build display output is a common pattern.
Example 11: Spread-Dot with Strings and Display
class Task {
String title
String status
String toString() { "[${status}] ${title}" }
}
def tasks = [
new Task(title: "Write blog post", status: "DONE"),
new Task(title: "Review PR", status: "IN_PROGRESS"),
new Task(title: "Deploy to staging", status: "TODO"),
new Task(title: "Run tests", status: "DONE")
]
// Spread-dot to get string representations
def taskStrings = tasks*.toString()
println "Tasks:"
taskStrings.each { println " ${it}" }
// Extract and format specific fields
def titles = tasks*.title
def statuses = tasks*.status
// Filter by status using spread
def doneTasks = tasks.findAll { it.status == "DONE" }*.title
println "\nCompleted: ${doneTasks}"
def pendingTasks = tasks.findAll { it.status != "DONE" }*.title
println "Pending: ${pendingTasks}"
// Build a summary with spread
def statusCounts = tasks*.status.countBy { it }
println "\nStatus summary: ${statusCounts}"
// Join spread results for display
println "All titles: ${tasks*.title.join(', ')}"
Output
Tasks: [DONE] Write blog post [IN_PROGRESS] Review PR [TODO] Deploy to staging [DONE] Run tests Completed: [Write blog post, Run tests] Pending: [Review PR, Deploy to staging] Status summary: [DONE:2, IN_PROGRESS:1, TODO:1] All titles: Write blog post, Review PR, Deploy to staging, Run tests
The tasks*.status.countBy { it } pattern is a beautiful one-liner for generating frequency counts. Extract the field with spread-dot, then use countBy to tally.
Spread Operator vs collect()
Since both the spread-dot and collect() produce a list of transformed values, when should you use each one?
Spread-Dot vs collect()
def names = ["alice", "bob", "charlie"]
// SPREAD-DOT: best for single method call or property access
def upper1 = names*.toUpperCase()
def lens1 = names*.length()
// COLLECT: best for complex transformations
def upper2 = names.collect { it.toUpperCase() }
def formatted = names.collect { "${it.capitalize()} (${it.length()} chars)" }
// When to use spread-dot:
// 1. Single method call: list*.method()
// 2. Single property access: list*.property
// 3. Simple chained access: list*.prop1*.prop2
// When to use collect:
// 1. Complex transformations: list.collect { complex logic }
// 2. Multiple operations: list.collect { it.trim().toUpperCase() }
// 3. Conditional logic: list.collect { it.length() > 3 ? it : it.padRight(4) }
println "Spread: ${upper1}"
println "Collect: ${upper2}"
println "Complex: ${formatted}"
// Performance: both are equivalent
// Spread-dot compiles to essentially the same bytecode as collect
// Choose based on readability, not performance
Output
Spread: [ALICE, BOB, CHARLIE] Collect: [ALICE, BOB, CHARLIE] Complex: [Alice (5 chars), Bob (3 chars), Charlie (7 chars)]
Rule of thumb: Use spread-dot (
*.) for simple, single-operation transformations. Usecollect()when the transformation involves multiple steps, conditionals, or complex logic. Both produce the same result type and have equivalent performance.
Real-World Use Cases
Use Case 1: Processing API Responses
When you receive a list of objects from an API, the spread operator makes data extraction trivial.
Real-World: API Response Processing
// Simulated API response (parsed JSON)
def apiResponse = [
[id: 1, title: "Introduction to Groovy", author: "Alice", tags: ["groovy", "beginner"]],
[id: 2, title: "Advanced Closures", author: "Bob", tags: ["groovy", "closures"]],
[id: 3, title: "Grails REST APIs", author: "Alice", tags: ["grails", "rest"]],
[id: 4, title: "Spock Testing", author: "Charlie", tags: ["groovy", "testing"]]
]
// Quick data extraction
def titles = apiResponse*.title
println "Titles: ${titles}"
def authors = apiResponse*.author.unique().sort()
println "Authors: ${authors}"
def allTags = apiResponse*.tags.flatten().unique().sort()
println "All tags: ${allTags}"
// Filter and extract
def alicePosts = apiResponse.findAll { it.author == "Alice" }*.title
println "\nAlice's posts: ${alicePosts}"
def groovyPosts = apiResponse.findAll { "groovy" in it.tags }*.title
println "Groovy posts: ${groovyPosts}"
// Build a tag-to-post index
def tagIndex = [:]
apiResponse.each { post ->
post.tags.each { tag ->
tagIndex.getOrDefault(tag, [])
if (!tagIndex[tag]) tagIndex[tag] = []
tagIndex[tag] << post.title
}
}
println "\nTag index:"
tagIndex.each { tag, posts -> println " ${tag}: ${posts}" }
Output
Titles: [Introduction to Groovy, Advanced Closures, Grails REST APIs, Spock Testing] Authors: [Alice, Bob, Charlie] All tags: [beginner, closures, grails, groovy, rest, testing] Alice's posts: [Introduction to Groovy, Grails REST APIs] Groovy posts: [Introduction to Groovy, Advanced Closures, Spock Testing] Tag index: groovy: [Introduction to Groovy, Advanced Closures, Spock Testing] beginner: [Introduction to Groovy] closures: [Advanced Closures] grails: [Grails REST APIs] rest: [Grails REST APIs] testing: [Spock Testing]
Use Case 2: CSV and Data Processing
The spread operator makes CSV parsing and data transformation clean and readable.
Real-World: CSV Data Processing
// Parse CSV-like data
def csvLines = [
" Alice , 30 , Engineering , 95000 ",
" Bob , 25 , Marketing , 75000 ",
" Charlie , 35 , Engineering , 105000 ",
" Diana , 28 , Marketing , 80000 "
]
// Parse each line: split, trim all fields
def records = csvLines.collect { line ->
def fields = line.split(',')*.trim()
[name: fields[0], age: fields[1].toInteger(),
dept: fields[2], salary: fields[3].toInteger()]
}
println "Records:"
records.each { println " ${it}" }
// Analytics using spread
def avgSalary = records*.salary.sum() / records.size()
println "\nAverage salary: ${avgSalary}"
def departments = records*.dept.unique()
println "Departments: ${departments}"
// Group and analyze
def byDept = records.groupBy { it.dept }
byDept.each { dept, members ->
def avgPay = members*.salary.sum() / members.size()
println "${dept}: ${members*.name} (avg: ${avgPay})"
}
Output
Records: [name:Alice, age:30, dept:Engineering, salary:95000] [name:Bob, age:25, dept:Marketing, salary:75000] [name:Charlie, age:35, dept:Engineering, salary:105000] [name:Diana, age:28, dept:Marketing, salary:80000] Average salary: 88750 Departments: [Engineering, Marketing] Engineering: [Alice, Charlie] (avg: 100000) Marketing: [Bob, Diana] (avg: 77500)
The line.split(',')*.trim() pattern is the cleanest way to parse CSV fields in Groovy. Split by delimiter, trim every field in one shot. No loops, no temporary variables.
Edge Cases and Best Practices
Important Edge Cases
- Null elements:
[1, null, 3]*.toString()throwsNullPointerException. Filter nulls first or usecollectwith?. - Null collection:
nullList*.method()returnsnull, not an empty list - Return type: The result is always an
ArrayList, even if the input was a Set, array, or range - Performance: Equivalent to
collect— no faster, no slower - Void methods: Spread-dot with void methods returns a list of nulls. Use
eachfor side-effect-only operations instead.
Best Practices Summary
- Use
*.for single method calls or property access:list*.name - Use
collectfor complex transformations with multiple steps - Always handle null elements before using
*.with methods that cannot be called on null - Use
[*:map1, *:map2]for clean configuration merging - Chain
*.withsort(),unique(),findAll()for powerful one-line data processing
Conclusion
The Groovy spread operator (*.) is one of those features that makes Groovy code so expressive compared to Java. From extracting property values from a list of objects to calling a method on every element or merging maps for configuration, the spread operator gives you a concise, readable way to do it.
Combined with the Elvis operator for defaults and the safe navigation operator for null safety, these three operators form the core of idiomatic Groovy programming. Master them, and your Groovy code will be dramatically shorter and easier to read.
Summary
list*.method()calls a method on every element, returning a list of resultslist*.propertyextracts a property from every element*listunpacks a list into individual method arguments[*:map1, *:map2]merges maps with last-wins semantics- Watch out for null elements — spread-dot does not handle them automatically
- Use
collectwhen you need complex multi-step transformations
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 Spaceship Operator (<=>) – Compare Anything
Frequently Asked Questions
What is the spread operator in Groovy?
The Groovy spread operator (*.) is also called the spread-dot operator. It calls a method or accesses a property on every element in a collection and returns a list of results. For example, ['alice', 'bob']*.toUpperCase() returns ['ALICE', 'BOB']. It is a concise alternative to the collect() method for simple, single-operation transformations.
What is the difference between the spread operator (*.) and collect() in Groovy?
Both produce a list of transformed values. The spread operator (*.) is best for single method calls or property access (list*.name). The collect() method is better for complex transformations involving multiple steps, conditionals, or custom logic. Performance is identical — choose based on readability.
Does the Groovy spread operator handle null elements?
No. If a list contains null elements, calling a method via spread-dot (list*.method()) will throw a NullPointerException when it encounters the null. To handle null elements, either filter them first with findAll() or use collect with the safe navigation operator: list.collect { it?.method() }.
What happens when you use the spread operator on a null list in Groovy?
When the list itself is null, the spread-dot operator returns null (not an empty list). For example, if List items = null, then items*.name returns null. This is consistent with Groovy’s safe navigation behavior. If you need an empty list as default, add the Elvis operator: items*.name ?: [].
How do I merge maps using the spread operator in Groovy?
Use the map spread syntax: [*:map1, *:map2]. For example, [*:[a:1, b:2], *:[b:3, c:4]] produces [a:1, b:3, c:4]. The last spread wins for duplicate keys. This is commonly used for merging configuration defaults with overrides in Gradle build scripts and Grails applications.
Related Posts
Previous in Series: Groovy Safe Navigation Operator (?.) – Avoid NullPointerException
Next in Series: Groovy Spaceship Operator (<=>) – Compare Anything
Related Topics You Might Like:
- Groovy Elvis Operator (?:) – Default Values Made Easy
- Groovy Safe Navigation Operator (?.) – Avoid NullPointerException
- Groovy Compare Strings – Methods and Best Practices
This post is part of the Groovy & Grails Cookbook series on TechnoScripts.com

No comment