What you'll learn

  • List literals and operators (<<, +, -, in)
  • Map literals and property-style access
  • Ranges and slicing
  • The spread operator *. for nested data

Groovy’s collection syntax is one of its biggest wins over Java. Lists and maps have literals, operators and indexes that make working with data — especially JSON-shaped data — feel natural.

Lists

def skus = ['AX-200', 'BX-310']        // an ArrayList
skus << 'CX-420'                        // append
skus += 'DX-530'
skus[0]                                 // AX-200
skus[-1]                                // DX-530 — last element
skus[1..2]                              // [BX-310, CX-420] — a slice
'BX-310' in skus                        // true
[3, 1, 2].sort()                        // [1, 2, 3]
[1, 2, 2, 3].unique()                   // [1, 2, 3]
[1, 2, 3] - [2]                         // [1, 3]

Maps

def stock = ['AX-200': 12, 'BX-310': 0]          // a LinkedHashMap: keeps insertion order
stock['CX-420'] = 7
stock.'DX-530' = 3
stock.get('ZZ-999', 0)                            // 0 — and stores it as the default

def order = [id: 'PO-4471', customer: 'Acme']     // simple keys need no quotes
order.customer                                    // Acme — property-style access
order.each { key, value -> println "$key -> $value" }

Watch out: [id: 'x'] uses the string 'id' as the key, not a variable called id. To use a variable’s value as a key, wrap it in parentheses: [(key): value].

An empty map is [:]; an empty list is [].

Ranges

(1..5).toList()          // [1, 2, 3, 4, 5]
(1..<5).toList()         // [1, 2, 3, 4] — exclusive end
('a'..'e').join('')      // abcde
for (i in 1..3) { ... }

Ranges also work as list and string indexes, as you saw above.

Nested data and the spread operator

Real payloads are nested — maps inside lists inside maps, just like JSON:

def payload = [orders: [[id: 'PO-1', total: 250], [id: 'PO-2', total: 90]]]
payload.orders*.id            // [PO-1, PO-2] — "id of every element"
payload.orders.total.sum()    // 340 — property access on a list collects from every element

*. (spread-dot) calls a property or method on every element of a list. You’ll use it constantly with parsed JSON in Working with JSON and XML.

The full example

// collections.groovy — lists, maps and ranges with Groovy syntax.

// Lists: [] creates an ArrayList
def skus = ['AX-200', 'BX-310']
skus << 'CX-420'                          // append
skus += 'DX-530'
println skus                              // [AX-200, BX-310, CX-420, DX-530]
println skus[0] + ' ' + skus[-1]          // first and last
println skus[1..2]                        // [BX-310, CX-420]
println skus.size()
println 'BX-310' in skus                  // true
println([3, 1, 2].sort())                 // [1, 2, 3]
println([1, 2, 2, 3].unique())            // [1, 2, 3]
println([1, 2, 3] - [2])                  // [1, 3]

// Maps: [key: value] creates a LinkedHashMap (keeps insertion order)
def stock = ['AX-200': 12, 'BX-310': 0]
stock['CX-420'] = 7                       // add
stock.'DX-530' = 3                        // property-style access
println stock                             // [AX-200:12, BX-310:0, CX-420:7, DX-530:3]
println stock['AX-200']                   // 12
println stock.get('ZZ-999', 0)            // 0 (and stores it)

def order = [id: 'PO-4471', customer: 'Acme', lines: 3]   // simple keys need no quotes
println order.customer                    // Acme
order.each { key, value -> println "$key -> $value" }

// Ranges
def range = 1..5
println range.toList()                    // [1, 2, 3, 4, 5]
println((1..<5).toList())                 // [1, 2, 3, 4]  (exclusive end)
println(('a'..'e').join(''))              // abcde
for (i in 1..3) print "$i "
println()

// Nested data, like JSON
def payload = [orders: [[id: 'PO-1', total: 250], [id: 'PO-2', total: 90]]]
println payload.orders*.id                // [PO-1, PO-2]  (spread operator)
println payload.orders.total.sum()        // 340

Try it yourself

Given def lines = [[sku: 'AX-200', qty: 2], [sku: 'BX-310', qty: 5], [sku: 'AX-200', qty: 1]], build a map from SKU to total quantity.

Show a solution
def lines = [[sku: 'AX-200', qty: 2], [sku: 'BX-310', qty: 5], [sku: 'AX-200', qty: 1]]
def totals = [:]
lines.each { totals[it.sku] = (totals[it.sku] ?: 0) + it.qty }
println totals   // [AX-200:3, BX-310:5]

In Functional collection methods you’ll do this with groupBy in one line.

Key takeaways

  • [] makes lists, [k: v] makes (ordered) maps; <<, +, - and in work on them.
  • Negative indexes and ranges slice lists and strings.
  • Map keys in literals are strings; use (var) for a variable key.
  • *. and property access on lists make nested data easy to navigate.

Example files