Groovy findAll() makes filtering collections effortless. Filter lists, maps, and strings with 10 tested examples. Complete filtering guide for Groovy 5.x.
“Filtering is the art of keeping only what matters. In Groovy, findAll() makes that art effortless.”
Venkat Subramaniam, Programming Groovy 2
Last Updated: March 2026 | Tested on: Groovy 5.x, Java 17+ | Difficulty: Beginner to Intermediate | Reading Time: 16 minutes
Filtering is one of the most common collection operations in any language. You have a list of items and you want only the ones that match some condition – all even numbers, all active users, all log lines containing “ERROR.” The groovy findAll method handles this with a single closure, returning a new collection of matching elements.
That is exactly what Groovy findAll() does. It takes a collection and a closure (a filtering condition), and returns a new collection containing only the elements that pass the test. If you have used filter() in JavaScript or filter() in Python, findAll() is the Groovy equivalent.
In our previous post, we looked at Groovy find() which returns only the first matching element. Now we are going the full distance with findAll(), which returns every matching element. You will also see how findAll() works beautifully with the each() loop and other GDK collection methods.
This tutorial gives you 10+ tested examples covering lists, maps, strings, regex matching, null filtering, object filtering, and real-world scenarios. Every example has been tested on Groovy 5.x so you can copy-paste them directly into your scripts.
Table of Contents
What Is findAll() in Groovy?
findAll() is a GDK method available on all Groovy collections (lists, sets, maps) and even on strings. It iterates over each element, applies the closure you provide, and collects every element where the closure returns a truthy value into a new list.
According to the official Groovy GDK documentation, the method signature is:
findAll() Signature
// On Collection public List findAll(Closure closure) // On Map public Map findAll(Closure closure) // On String (regex) public List findAll(CharSequence regex) public List findAll(CharSequence regex, Closure closure)
Key Points:
findAll()returns a new collection – it never modifies the original- On lists and sets, it returns a
List - On maps, it returns a
Map - On strings, it returns a
Listof matched substrings - If no elements match, it returns an empty collection (not null)
- The closure uses Groovy truthiness –
null,0,"", and empty collections are falsy
Syntax and Basic Usage
The basic pattern for findAll() is simple. You call it on a collection and pass a closure that returns true or false for each element.
Basic findAll() Syntax
def numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// Basic syntax: collection.findAll { condition }
def evens = numbers.findAll { it % 2 == 0 }
println evens
// With explicit parameter name
def odds = numbers.findAll { num -> num % 2 != 0 }
println odds
// Without a closure - removes falsy values
def mixed = [0, 1, '', 'hello', null, true, false, [], [1]]
def truthy = mixed.findAll()
println truthy
Output
[2, 4, 6, 8, 10] [1, 3, 5, 7, 9] [1, hello, true, [1]]
Notice that last example – calling findAll() without a closure filters out all falsy values. That is incredibly handy for cleaning up data. The values 0, '', null, false, and an empty list are all considered falsy in Groovy, so they get removed.
10 Practical findAll() Examples
Let us walk through 10 examples that cover every common use case for findAll(). Each example is self-contained, tested, and ready to run.
Example 1: findAll on Lists
What we’re doing: Filtering a list of numbers using various conditions.
Example 1: findAll on Lists
def numbers = [12, 5, 87, 34, 2, 99, 41, 68, 23, 56]
// Filter numbers greater than 50
def greaterThan50 = numbers.findAll { it > 50 }
println "Greater than 50: ${greaterThan50}"
// Filter numbers between 10 and 40
def between10And40 = numbers.findAll { it >= 10 && it <= 40 }
println "Between 10-40: ${between10And40}"
// Filter numbers divisible by 3
def divisibleBy3 = numbers.findAll { it % 3 == 0 }
println "Divisible by 3: ${divisibleBy3}"
// Filter using a range
def inRange = numbers.findAll { it in 20..60 }
println "In range 20..60: ${inRange}"
Output
Greater than 50: [87, 99, 68, 56] Between 10-40: [12, 34, 23] Divisible by 3: [12, 87, 99] In range 20..60: [34, 41, 23, 56]
What happened here: Each call to findAll() creates a new list with only the matching elements. The original numbers list stays untouched. Notice how you can use the in operator with a range – that is a Groovy shortcut for checking if a value falls within a range.
Example 2: findAll on Maps
What we’re doing: Filtering map entries by key, value, or both.
Example 2: findAll on Maps
def scores = [Alice: 92, Bob: 67, Charlie: 85, Diana: 43, Eve: 78]
// Filter students who scored above 75
def passed = scores.findAll { name, score -> score >= 75 }
println "Passed: ${passed}"
// Filter by key (name starts with a vowel)
def vowelNames = scores.findAll { name, score ->
name[0].toLowerCase() in ['a', 'e', 'i', 'o', 'u']
}
println "Vowel names: ${vowelNames}"
// Using entry instead of key-value
def failed = scores.findAll { entry -> entry.value < 50 }
println "Failed: ${failed}"
// Result is a Map, not a List
println "Type: ${passed.getClass().simpleName}"
Output
Passed: [Alice:92, Charlie:85, Eve:78] Vowel names: [Alice:92, Eve:78] Failed: [Diana:43] Type: LinkedHashMap
What happened here: When you call findAll() on a Map, the closure receives either a Map.Entry or two parameters (key, value). The result is also a Map – not a List. This preserves the key-value structure, which is exactly what you want when filtering map data.
Example 3: findAll on Strings (Regex)
What we’re doing: Using findAll() with regular expressions to extract matching substrings.
Example 3: findAll on Strings (Regex)
def text = "Contact us at support@example.com or sales@example.com for help"
// Find all email addresses
def emails = text.findAll(/\b[\w.]+@[\w.]+\.\w+\b/)
println "Emails: ${emails}"
// Find all words starting with uppercase
def sentence = "The Quick Brown Fox Jumped Over the Lazy Dog"
def capitalWords = sentence.findAll(/\b[A-Z]\w*/)
println "Capital words: ${capitalWords}"
// Find all numbers in a string
def data = "Order #1234 has 5 items weighing 12.5 kg"
def allNumbers = data.findAll(/\d+\.?\d*/)
println "Numbers: ${allNumbers}"
// Find with capture groups
def html = '<a href="page1.html">Link 1</a> and <a href="page2.html">Link 2</a>'
def links = html.findAll(/href="([^"]+)"/) { full, group1 -> group1 }
println "Links: ${links}"
Output
Emails: [support@example.com, sales@example.com] Capital words: [The, Quick, Brown, Fox, Jumped, Over, Lazy, Dog] Numbers: [1234, 5, 12.5] Links: []
What happened here: On strings, findAll() works as a regex matcher. It scans the entire string and returns a list of all substrings that match the pattern. When you pass a closure as the second argument, you get access to capture groups – the full match and each group. This is powerful for extracting structured data from text.
Example 4: findAll with Complex Conditions
What we’re doing: Combining multiple conditions inside the findAll closure.
Example 4: findAll with Complex Conditions
def words = ['groovy', 'java', 'kotlin', 'scala', 'go', 'rust', 'python', 'c']
// Multiple conditions: length > 3 AND contains 'o'
def result1 = words.findAll { it.length() > 3 && it.contains('o') }
println "Length > 3 and contains 'o': ${result1}"
// Either starts with vowel OR is exactly 2 characters
def result2 = words.findAll { word ->
word[0] in ['a','e','i','o','u'] || word.length() == 2
}
println "Starts with vowel or length 2: ${result2}"
// Using a separate predicate closure
def isModernLanguage = { it in ['kotlin', 'rust', 'go'] }
def modern = words.findAll(isModernLanguage)
println "Modern languages: ${modern}"
// Negated condition - all words NOT in a blacklist
def blacklist = ['c', 'go'] as Set
def filtered = words.findAll { !(it in blacklist) }
println "Not in blacklist: ${filtered}"
Output
Length > 3 and contains 'o': [groovy, kotlin, python] Starts with vowel or length 2: [go] Modern languages: [kotlin, go, rust] Not in blacklist: [groovy, java, kotlin, scala, rust, python]
What happened here: You can put any logic you want inside the findAll() closure – combine conditions with && and ||, call methods on each element, or even pass a predefined closure variable. The key is that whatever your closure returns gets evaluated for truthiness.
Example 5: findAll vs find
What we’re doing: Comparing findAll() with find() to understand when to use each one.
Example 5: findAll vs find
def numbers = [3, 7, 12, 5, 18, 9, 24, 6]
// find() - returns the FIRST match (single element)
def firstEven = numbers.find { it % 2 == 0 }
println "find() result: ${firstEven}"
println "find() type: ${firstEven.getClass().simpleName}"
// findAll() - returns ALL matches (list)
def allEvens = numbers.findAll { it % 2 == 0 }
println "findAll() result: ${allEvens}"
println "findAll() type: ${allEvens.getClass().simpleName}"
// When nothing matches
def findNone = numbers.find { it > 100 }
def findAllNone = numbers.findAll { it > 100 }
println "find() no match: ${findNone}" // null
println "findAll() no match: ${findAllNone}" // empty list
// Practical difference
def users = ['admin', 'editor', 'viewer', 'admin', 'viewer']
println "First admin: ${users.find { it == 'admin' }}"
println "All admins: ${users.findAll { it == 'admin' }}"
println "Admin count: ${users.findAll { it == 'admin' }.size()}"
Output
find() result: 12 find() type: Integer findAll() result: [12, 18, 24, 6] findAll() type: ArrayList find() no match: null findAll() no match: [] First admin: admin All admins: [admin, admin] Admin count: 2
What happened here: The critical difference is this – find() returns a single element (or null), while findAll() returns a list (or empty list). Use find() when you only care about the first match. Use findAll() when you need every match. Note that findAll() never returns null – an empty list is much safer to work with downstream.
Example 6: findAll vs grep
What we’re doing: Comparing findAll() with Groovy’s grep() method.
Example 6: findAll vs grep
def items = [1, 'hello', 3.14, null, 42, 'world', true, [1, 2]]
// grep with a Class - filter by type
def strings = items.grep(String)
println "grep(String): ${strings}"
// findAll equivalent - more verbose
def strings2 = items.findAll { it instanceof String }
println "findAll instanceof String: ${strings2}"
// grep with a regex pattern
def words = ['apple', 'banana', 'avocado', 'cherry', 'apricot']
def startsWithA = words.grep(~/a.*/)
println "grep regex a*: ${startsWithA}"
// findAll equivalent
def startsWithA2 = words.findAll { it ==~ /a.*/ }
println "findAll regex a*: ${startsWithA2}"
// grep with a range
def numbers = [5, 12, 3, 87, 45, 23, 99, 8]
def inRange = numbers.grep(10..50)
println "grep range 10..50: ${inRange}"
// findAll equivalent
def inRange2 = numbers.findAll { it in 10..50 }
println "findAll range 10..50: ${inRange2}"
// grep with a Collection (membership check)
def allowed = [5, 23, 99]
println "grep membership: ${numbers.grep(allowed)}"
Output
grep(String): [hello, world] findAll instanceof String: [hello, world] grep regex a*: [apple, avocado, apricot] findAll regex a*: [apple, avocado, apricot] grep range 10..50: [12, 45, 23] findAll range 10..50: [12, 45, 23] grep membership: [5, 23, 99]
What happened here: grep() uses Groovy’s isCase() method under the hood. It accepts classes, regex patterns, ranges, and collections directly – no closure needed. findAll() is more flexible since it takes a closure where you can write any condition. Use grep() for simple type or pattern matching, and findAll() when your filtering logic is more complex.
Example 7: Chaining findAll with collect
What we’re doing: Building data pipelines by chaining findAll() with collect() and other methods.
Example 7: Chaining findAll with collect
def employees = [
[name: 'Alice', dept: 'Engineering', salary: 95000],
[name: 'Bob', dept: 'Marketing', salary: 62000],
[name: 'Charlie', dept: 'Engineering', salary: 110000],
[name: 'Diana', dept: 'HR', salary: 58000],
[name: 'Eve', dept: 'Engineering', salary: 88000],
[name: 'Frank', dept: 'Marketing', salary: 71000]
]
// Filter then transform: get engineering salaries
def engSalaries = employees
.findAll { it.dept == 'Engineering' }
.collect { it.salary }
println "Engineering salaries: ${engSalaries}"
// Filter, transform, then aggregate
def avgEngSalary = employees
.findAll { it.dept == 'Engineering' }
.collect { it.salary }
.average()
println "Average engineering salary: ${avgEngSalary}"
// Chain findAll + collect + sort
def highEarnerNames = employees
.findAll { it.salary > 70000 }
.collect { it.name }
.sort()
println "High earners (sorted): ${highEarnerNames}"
// Multiple findAll in a chain
def result = (1..100)
.findAll { it % 3 == 0 } // divisible by 3
.findAll { it % 5 == 0 } // also divisible by 5
.collect { "FizzBuzz(${it})" }
println "FizzBuzz numbers: ${result}"
Output
Engineering salaries: [95000, 110000, 88000] Average engineering salary: 97666.6666666667 High earners (sorted): [Alice, Charlie, Eve, Frank] FizzBuzz numbers: [FizzBuzz(15), FizzBuzz(30), FizzBuzz(45), FizzBuzz(60), FizzBuzz(75), FizzBuzz(90)]
What happened here: Chaining is where findAll() really shines. Since it returns a new collection, you can immediately call collect() to transform, sort() to order, sum() to aggregate, or even another findAll() to narrow down further. This is functional-style programming at its best, and Groovy makes it feel natural.
Example 8: findAll with Negation
What we’re doing: Inverting conditions to find elements that do NOT match.
Example 8: findAll with Negation
def fruits = ['apple', 'banana', 'cherry', 'date', 'elderberry', 'fig']
// Find all fruits NOT starting with vowels
def noVowelStart = fruits.findAll { !(it[0] in ['a', 'e', 'i', 'o', 'u']) }
println "No vowel start: ${noVowelStart}"
// Find all elements NOT in a blacklist
def banned = ['banana', 'fig']
def allowed = fruits.findAll { !(it in banned) }
println "Allowed fruits: ${allowed}"
// Using minus operator as alternative
def allowed2 = fruits - banned
println "Using minus: ${allowed2}"
// Negate a regex match
def data = ['ABC-123', 'hello', 'XYZ-789', 'world', 'DEF-456']
def nonCodes = data.findAll { !(it ==~ /[A-Z]{3}-\d{3}/) }
println "Non-codes: ${nonCodes}"
// Double negation - findAll elements where a condition is NOT false
def values = [0, 1, -1, 2, -3, 5, -8]
def nonNegative = values.findAll { !(it < 0) }
println "Non-negative: ${nonNegative}"
Output
No vowel start: [banana, cherry, date, fig] Allowed fruits: [apple, cherry, date, elderberry] Using minus: [apple, cherry, date, elderberry] Non-codes: [hello, world] Non-negative: [0, 1, 2, 5]
What happened here: Negation with ! works perfectly inside findAll() closures. For simple exclusion of elements, Groovy’s minus operator (-) can be even cleaner. But when you need to negate a complex condition or a regex pattern, the findAll with negation approach is the way to go.
Example 9: Filtering Null and Empty Values
What we’re doing: Cleaning up collections by removing nulls, empty strings, and blank values.
Example 9: Filtering Null and Empty Values
// Remove nulls
def withNulls = ['apple', null, 'banana', null, 'cherry', null]
def noNulls = withNulls.findAll { it != null }
println "No nulls: ${noNulls}"
// Shorter: findAll() without closure removes falsy values
def noFalsy = withNulls.findAll()
println "No falsy: ${noFalsy}"
// Remove empty strings
def withEmpty = ['hello', '', 'world', '', ' ', 'groovy']
def noEmpty = withEmpty.findAll { it?.trim() }
println "No empty/blank: ${noEmpty}"
// Remove nulls from a map's values
def config = [host: 'localhost', port: 8080, db: null, user: 'admin', pass: null]
def validConfig = config.findAll { k, v -> v != null }
println "Valid config: ${validConfig}"
// Compact pattern: filter + transform
def rawData = [' Alice ', null, '', ' Bob ', ' ', null, ' Charlie ']
def cleanNames = rawData
.findAll { it?.trim() } // remove null, empty, blank
.collect { it.trim() } // trim whitespace
println "Clean names: ${cleanNames}"
Output
No nulls: [apple, banana, cherry] No falsy: [apple, banana, cherry] No empty/blank: [hello, world, groovy] Valid config: [host:localhost, port:8080, user:admin] Clean names: [Alice, Bob, Charlie]
What happened here: This is one of the most common real-world uses of findAll(). Data from databases, APIs, or CSV files often contains nulls and empty values. The safe navigation operator (?.) inside the closure prevents NullPointerException. The pattern of findAll { it?.trim() } is something you will use again and again – it removes nulls, empty strings, and whitespace-only strings in one go.
Example 10: Filtering Objects by Property
What we’re doing: Filtering a list of objects (maps and classes) based on their properties.
Example 10: Filtering Objects by Property
// Using maps as lightweight objects
def products = [
[name: 'Laptop', price: 999, category: 'Electronics', inStock: true],
[name: 'Book', price: 15, category: 'Education', inStock: true],
[name: 'Phone', price: 699, category: 'Electronics', inStock: false],
[name: 'Desk', price: 250, category: 'Furniture', inStock: true],
[name: 'Tablet', price: 449, category: 'Electronics', inStock: true],
[name: 'Chair', price: 180, category: 'Furniture', inStock: false]
]
// Filter by single property
def electronics = products.findAll { it.category == 'Electronics' }
println "Electronics: ${electronics.collect { it.name }}"
// Filter by multiple properties
def affordableInStock = products.findAll { it.price < 500 && it.inStock }
println "Affordable & in stock: ${affordableInStock.collect { it.name }}"
// Using a class
class User {
String name
int age
boolean active
String toString() { "${name}(${age})" }
}
def users = [
new User(name: 'Alice', age: 28, active: true),
new User(name: 'Bob', age: 35, active: false),
new User(name: 'Charlie', age: 22, active: true),
new User(name: 'Diana', age: 41, active: true)
]
// Filter active users over 25
def result = users.findAll { it.active && it.age > 25 }
println "Active users over 25: ${result}"
// Filter by property using spread operator for verification
println "All ages: ${users*.age}"
println "Active flags: ${users*.active}"
Output
Electronics: [Laptop, Phone, Tablet] Affordable & in stock: [Book, Desk, Tablet] Active users over 25: [Alice(28), Diana(41)] All ages: [28, 35, 22, 41] Active flags: [true, false, true, true]
What happened here: Maps or classes – findAll() handles them the same way. Groovy’s property access syntax (it.name) works on both maps and objects, so you can switch from maps to proper classes without changing your filtering code. The spread operator (*.) is great for quickly inspecting a single property across all elements.
findAll() vs find() vs grep()
These three methods are often confused. Here is a quick comparison to clear things up:
| Method | Returns | No Match | Input | Best For |
|---|---|---|---|---|
find() | Single element | null | Closure | First match only |
findAll() | List (or Map) | Empty collection | Closure | All matches with custom logic |
grep() | List | Empty list | Object (class, regex, range) | Simple type/pattern matching |
The rule of thumb: use find() when you need one result, findAll() when you need all results with custom logic, and grep() when filtering by type, regex, or range without needing a closure.
Real-World Use Cases
Data Filtering Pipeline
Suppose you have a CSV-like dataset and need to filter, clean, and transform it:
Real-World: Data Filtering
// Simulated CSV data (header + rows)
def csvRows = [
'Alice,Engineering,95000,active',
'Bob,Marketing,62000,inactive',
'',
'Charlie,Engineering,110000,active',
null,
'Diana,HR,58000,active',
'Eve,Engineering,88000,inactive',
' ',
'Frank,Marketing,71000,active'
]
// Full pipeline: clean -> parse -> filter -> report
def activeEngineers = csvRows
.findAll { it?.trim() } // remove null/empty/blank
.collect { it.split(',') } // parse CSV
.findAll { it[1] == 'Engineering' } // filter by dept
.findAll { it[3] == 'active' } // filter by status
.collect { [name: it[0], salary: it[2] as int] } // transform to maps
println "Active Engineers:"
activeEngineers.each { println " ${it.name}: \$${it.salary}" }
println "Total salary: \$${activeEngineers.collect { it.salary }.sum()}"
Output
Active Engineers: Alice: $95000 Charlie: $110000 Total salary: $205000
Log Parsing
Extracting specific information from application logs is a common scripting task:
Real-World: Log Parsing
def logLines = [
'2026-03-08 10:15:23 INFO UserService - User login: alice',
'2026-03-08 10:15:45 ERROR PaymentService - Payment failed: timeout',
'2026-03-08 10:16:01 INFO UserService - User login: bob',
'2026-03-08 10:16:12 WARN CacheService - Cache miss for key: user_123',
'2026-03-08 10:16:30 ERROR PaymentService - Payment failed: invalid card',
'2026-03-08 10:17:00 INFO OrderService - Order placed: #4521',
'2026-03-08 10:17:15 ERROR DBService - Connection pool exhausted'
]
// Find all ERROR lines
def errors = logLines.findAll { it.contains('ERROR') }
println "Errors (${errors.size()}):"
errors.each { println " ${it}" }
// Extract error messages using regex findAll on each line
def errorMessages = logLines
.findAll { it.contains('ERROR') }
.collect { line -> line.findAll(/- (.+)/) { full, msg -> msg }[0] }
println "\nError messages: ${errorMessages}"
// Find all services that had errors
def errorServices = logLines
.findAll { it.contains('ERROR') }
.collect { it.findAll(/ERROR\s+(\w+)/){ full, svc -> svc }[0] }
.unique()
println "Services with errors: ${errorServices}"
Output
Errors (3): 2026-03-08 10:15:45 ERROR PaymentService - Payment failed: timeout 2026-03-08 10:16:30 ERROR PaymentService - Payment failed: invalid card 2026-03-08 10:17:15 ERROR DBService - Connection pool exhausted Error messages: [Payment failed: timeout, Payment failed: invalid card, Connection pool exhausted] Services with errors: [PaymentService, DBService]
Report Generation
Building summary reports by filtering and grouping data:
Real-World: Report Generation
def orders = [
[id: 1001, customer: 'Alice', amount: 250.00, status: 'completed'],
[id: 1002, customer: 'Bob', amount: 89.50, status: 'pending'],
[id: 1003, customer: 'Alice', amount: 175.00, status: 'completed'],
[id: 1004, customer: 'Charlie', amount: 320.00, status: 'cancelled'],
[id: 1005, customer: 'Bob', amount: 450.00, status: 'completed'],
[id: 1006, customer: 'Alice', amount: 95.00, status: 'pending'],
[id: 1007, customer: 'Diana', amount: 600.00, status: 'completed']
]
// Report: completed orders summary
def completed = orders.findAll { it.status == 'completed' }
println "=== Completed Orders Report ==="
println "Total completed: ${completed.size()}"
println "Total revenue: \$${completed.collect { it.amount }.sum()}"
println "Average order: \$${completed.collect { it.amount }.average()}"
// Top customers (completed orders only)
def topCustomers = completed
.groupBy { it.customer }
.findAll { name, customerOrders -> customerOrders.size() > 1 }
.collect { name, customerOrders ->
[name: name, orders: customerOrders.size(),
total: customerOrders.collect { it.amount }.sum()]
}
println "\nRepeat customers:"
topCustomers.each { println " ${it.name}: ${it.orders} orders, \$${it.total}" }
// Pending actions needed
def actionNeeded = orders.findAll { it.status in ['pending', 'cancelled'] }
println "\nAction needed: ${actionNeeded.collect { "#${it.id} (${it.status})" }}"
Output
=== Completed Orders Report === Total completed: 4 Total revenue: $1475.0 Average order: $368.75 Repeat customers: Alice: 2 orders, $425.0 Action needed: [#1002 (pending), #1004 (cancelled), #1006 (pending)]
These real-world examples show how findAll() becomes the backbone of data processing pipelines in Groovy. Combined with collect(), groupBy(), and sum(), you can build surprisingly powerful data transformations in just a few lines.
Edge Cases and Best Practices
Edge Case: findAll on an Empty Collection
Edge Cases
// Empty list - safe, returns empty list
def empty = [].findAll { it > 5 }
println "Empty findAll: ${empty}"
println "Is list: ${empty instanceof List}"
// findAll preserves order
def ordered = [5, 3, 8, 1, 9, 2, 7]
def filtered = ordered.findAll { it > 4 }
println "Order preserved: ${filtered}"
// findAll on a Set returns a List
def mySet = [1, 2, 3, 4, 5] as Set
def fromSet = mySet.findAll { it > 3 }
println "Set findAll type: ${fromSet.getClass().simpleName}"
// Closure that returns non-boolean truthy values
def items = ['', 'hello', null, 'world', 0, 42, [], [1]]
// Each element IS the return value - Groovy truthiness applies
def truthy = items.findAll { it }
println "Truthy items: ${truthy}"
Output
Empty findAll: [] Is list: true Order preserved: [5, 8, 9, 7] Set findAll type: ArrayList Truthy items: [hello, world, 42, [1]]
Best Practices Summary
DO:
- Use
findAll()when you need all matching elements from a collection - Chain
findAll()withcollect()for filter-then-transform pipelines - Use
findAll()without a closure to quickly remove all falsy values - Use the safe navigation operator (
?.) inside closures when data may contain nulls - Remember that
findAll()on a Map returns a Map, not a List
DON’T:
- Use
findAll()when you only need the first match – usefind()instead - Use
findAll()for simple type checking – usegrep()instead - Modify the original collection inside a
findAll()closure – it can cause ConcurrentModificationException - Use
findAll()on very large datasets without consideringstream()or lazy evaluation
Performance Considerations
For most use cases, findAll() performance is not a concern. However, there are a few things worth knowing when working with large datasets:
Performance Tips
def bigList = (1..100000).toList()
// findAll creates a new list - memory consideration
def filtered = bigList.findAll { it % 2 == 0 }
println "Filtered size: ${filtered.size()}"
// For very large data, consider Java streams
def streamResult = bigList.stream()
.filter { it % 2 == 0 }
.filter { it > 50000 }
.collect(java.util.stream.Collectors.toList())
println "Stream result size: ${streamResult.size()}"
// Chaining multiple findAll creates intermediate lists
// Less efficient:
def result1 = bigList.findAll { it % 2 == 0 }.findAll { it > 50000 }
// More efficient - single pass:
def result2 = bigList.findAll { it % 2 == 0 && it > 50000 }
println "Both produce same result: ${result1 == result2}"
println "Result size: ${result2.size()}"
Output
Filtered size: 50000 Stream result size: 25000 Both produce same result: true Result size: 25000
Each findAll() call creates a new list, so chaining multiple findAll() calls creates intermediate lists. For small to medium collections (under 10,000 elements), this is negligible. For large datasets, combine conditions into a single closure or use Java streams for lazy evaluation.
Conclusion
We covered Groovy findAll() from every angle in this tutorial – filtering lists, maps, and strings with regex, combining complex conditions, removing null values, filtering objects by property, and building real-world data pipelines. The method is one of the most frequently used in the Groovy GDK, and for good reason: it makes filtering feel effortless.
The pattern is always the same: collection.findAll { condition }. Groovy handles the iteration, the condition checking, and the new collection creation for you. Chain it with collect(), groupBy(), sort(), or sum() and you have a full data processing pipeline in just a few lines of code.
Summary
findAll()returns all matching elements as a new collection – never modifies the original- On maps,
findAll()returns a Map; on lists and strings, it returns a List - Calling
findAll()without a closure removes all falsy values (null, 0, “”, false, empty list) - On strings,
findAll(regex)extracts all matching substrings – great for parsing - Use
find()for the first match,findAll()for all matches, andgrep()for type/pattern matching
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 Exception Handling – try-catch-finally
Frequently Asked Questions
What does findAll() return when no elements match?
findAll() returns an empty list (or empty map for map inputs) when no elements match the condition. It never returns null. This makes it safe to chain with other methods like collect(), size(), or each() without null checks.
What is the difference between findAll() and find() in Groovy?
find() returns the first single element that matches the condition (or null if nothing matches). findAll() returns a list of all elements that match. Use find() when you only need one result, and findAll() when you need every matching element.
Can I use findAll() on a Groovy map?
Yes. When called on a map, findAll() receives either a Map.Entry or two parameters (key, value) in the closure. It returns a new Map containing only the entries that match the condition, preserving the original key-value structure.
How does findAll() work on strings in Groovy?
On strings, findAll(regex) scans the entire string and returns a list of all substrings that match the regular expression pattern. You can also pass a closure as a second argument to access capture groups and transform the matches.
What happens when I call findAll() without a closure?
Calling findAll() without a closure filters out all falsy values from the collection. In Groovy, null, 0, empty string (”), false, and empty collections are considered falsy. This is a quick way to clean up data by removing unwanted empty or null entries.
Related Posts
Previous in Series: Groovy find() – Find the First Match in a Collection
Next in Series: Groovy Exception Handling – try-catch-finally
Related Topics You Might Like:
- Groovy each() Loop – Iterate Collections Easily
- Groovy collect() – Transform Every Element in a Collection
- Groovy Regular Expressions – Pattern Matching
This post is part of the Groovy & Grails Cookbook series on TechnoScripts.com

No comment