Groovy Syntax Tutorial Series: Collections (Part 6) [Final]


Groovy syntax overview

This tutorial series introduces the syntax of the Groovy programming language. Groovy’s syntax is derived from Java’s syntax, but it has been enhanced with specific types and permits certain simplifications.

Groovy collections comprise three types: lists, arrays and maps.

7. Lists

Groovy represents a list as a comma-separated list of values enclosed in square brackets.

Groovy lists are the JDK-native java.util.List; Groovy does not define collection classes of its own.

The concrete list implementation used when defining a list literal is java.util.ArrayList by default, unless you decide to specify otherwise, which we will also cover later.

def numbers = [1, 2, 3]    

assert numbers instanceof List
assert numbers.size() == 3
  • We define a list of numbers, comma-separated and enclosed in square brackets, and then assign the list to a variable
  • The list is an instance of Java’s java.util.List interface
  • The size of the list can be queried with the size() method; our list contains 3 elements

In the example above we used a list whose elements are all of the same type, but you can also create lists containing values of different types:

def heterogeneous = [1, "a", true]

This list contains a number, a string and a boolean.

We mentioned that, by default, a list literal is in fact an instance of java.util.ArrayList.

But you can also use type coercion with the as operator, or an explicit type declaration on the variable, to use a different backing type for the list:

def arrayList = [1, 2, 3]
assert arrayList instanceof java.util.ArrayList

def linkedList = [2, 3, 4] as LinkedList    
assert linkedList instanceof java.util.LinkedList

LinkedList otherLinked = [3, 4, 5]          
assert otherLinked instanceof java.util.LinkedList
  • Use type coercion with the as operator to make the list of type java.util.LinkedList

  • Use an explicit type declaration on the variable, declaring it as type java.util.LinkedList

You can access a list’s elements with the [] subscript operator using positive or negative indices (for both reading and setting values), access elements in forward or reverse order, and append elements to the list with the << operator:

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

assert letters[0] == 'a'    // 1
assert letters[1] == 'b'

assert letters[-1] == 'd'   // 2
assert letters[-2] == 'c'

letters[2] = 'C'             
assert letters[2] == 'C'    // 3

letters << 'e'              // 4
assert letters[4] == 'e'
assert letters[-1] == 'e'

assert letters[1, 3] == ['b', 'd']      // 5  
assert letters[2..4] == ['C', 'd', 'e'] // 6
  1. Access the first element of the list (zero-based counting)
  2. Access the last element of the list with a negative index: -1 is the first element from the end of the list
  3. Set a new value for the third element of the list
  4. Add an element to the end of the list with the << operator
  5. Access two elements at once, returning a new list containing those two elements
  6. Use a range to access the values of the list elements from the start to the end of the range

Because a list can hold values of different types, a list can also contain other lists to create multidimensional lists:

def multi = [[0, 1], [2, 3]]    // 1
assert multi[1][0] == 2         // 2
  1. Define a list whose elements are lists
  2. Access the first element of the second list element

8. Arrays

Groovy reuses the list notation for arrays. To create an array of a specific type, you need to define the array’s type explicitly through a cast or a type declaration.

String[] arrStr = ['Ananas', 'Banana', 'Kiwi']  // 1

assert arrStr instanceof String[]   // 2
assert !(arrStr instanceof List)

def numArr = [1, 2, 3] as int[]     // 3

assert numArr instanceof int[]      // 4
assert numArr.size() == 3
  1. Define a string array with an explicit variable type declaration
  2. Assert that we have created a string array
  3. Create an integer array with the as operator
  4. Assert that we have created a primitive integer array

You can also create multidimensional arrays:

def matrix3 = new Integer[3][3]         // 1
assert matrix3.size() == 3

Integer[][] matrix2                     // 2
matrix2 = [[1, 2], [3, 4]]
assert matrix2 instanceof Integer[][]
  1. You can define the bounds of a new array
  2. Or declare an array without specifying a size

Array elements are accessed in the same way as list elements:

String[] names = ['Cédric', 'Guillaume', 'Jochen', 'Paul']
assert names[0] == 'Cédric'     // 1

names[2] = 'Blackdrag'          // 2
assert names[2] == 'Blackdrag'
  1. Get the first element of the array
  2. Set the value of the third element of the array to a new value

Groovy does not support the Java array initialization notation, because braces conflict with Groovy’s closure notation.

Below is an example of a Java array initialization statement:

int a[] = {2, 0, 1, 9, 2020};

9. Maps

Sometimes called dictionaries or associative arrays in other languages, Groovy supports maps.

A map associates keys with values, separating a key from its value with a colon, separating each key/value pair with a comma, and enclosing the whole thing in square brackets.

def colors = [red: '#FF0000', green: '#00FF00', blue: '#0000FF']   // 1

assert colors['red'] == '#FF0000'    // 2
assert colors.green  == '#00FF00'    // 3

colors['pink'] = '#FF00FF'           // 4
colors.yellow  = '#FFFF00'           // 5

assert colors.pink == '#FF00FF'
assert colors['yellow'] == '#FFFF00'

assert colors instanceof java.util.LinkedHashMap
  1. We define a map of string color names associated with their hex-encoded HTML colors
  2. We use subscript notation to check what is associated with the red key
  3. We can also use property notation to state the hex representation of green
  4. Likewise, we can use subscript notation to add a new key/value pair
  5. Or use property notation to add yellow

When a name is used as a key, we are in fact defining a string-typed key in the map.

The maps Groovy creates are in fact instances of java.util.LinkedHashMap.

If you try to access a key that does not exist in the map, a null value is returned:

ssert colors.unknown == null

In the example above we used string-typed keys, but you can also use values of other types as keys:

def numbers = [1: 'one', 2: 'two']

assert numbers[1] == 'one'

Here we use numbers as keys; because a number can be unambiguously recognized as a number, Groovy does not create a string-typed key as in the previous example.

But consider the following case: you want to pass a variable in place of the key, so that the value of that variable becomes the key:

def key = 'name'
def person = [key: 'Guillaume']      // 1

assert !person.containsKey('name')   // 2
assert person.containsKey('key')     // 3
  1. The key associated with the name “Guillaume” is actually the “key” string, not the value associated with the key variable
  2. The map does not contain the 'name' key
  3. Instead, the map contains a 'key' key

You can also pass a quoted string as a key: ["name":"Guillaume"].

If your key string is not a valid identifier, this is mandatory: you must use quotes to declare it as a string.

For example, if you want to use a string key like this: ["street-name":"Main street"].

When you need to pass a variable value as a key in a map definition, you must enclose the variable or expression in parentheses:

person = [(key): 'Guillaume']        // 1

assert person.containsKey('name')    // 2
assert !person.containsKey('key')    // 3
  1. This time we enclose the key variable in parentheses to tell the parser to pass the variable rather than define a string key
  2. The map does contain the name key
  3. The map does not contain the key key as before