# Groovy Development Kit

version 6.0.0-beta-1

## 1. Working with IO

Groovy provides a number of helper methods for working with I/O. While you could use standard Java code in Groovy to deal with those, Groovy provides much more convenient ways to handle files, streams, readers, …​

In particular, you should take a look at methods added to:

- the `java.io.File` class : http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/File.html

- the `java.io.InputStream` class: http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/InputStream.html

- the `java.io.OutputStream` class: http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/OutputStream.html

- the `java.io.Reader` class: http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/Reader.html

- the `java.io.Writer` class: http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/Writer.html

- the `java.nio.file.Path` class: http://docs.groovy-lang.org/latest/html/groovy-jdk/java/nio/file/Path.html

The following section focuses on sample idiomatic constructs using helper methods available above but is not meant to be a complete description of all available methods. For that, please read the GDK API.

### 1.1. Reading files

As a first example, let’s see how you would print all lines of a text file in Groovy:

    new File(baseDir, 'haiku.txt').eachLine { line ->
        println line
    }

The `eachLine` method is a method added to the `File` class automatically by Groovy and has many variants, for example if you need to know the line number, you can use this variant:

    new File(baseDir, 'haiku.txt').eachLine { line, nb ->
        println "Line $nb: $line"
    }

If for whatever reason an exception is thrown in the `eachLine` body, the method makes sure that the resource is properly closed. This is true for all I/O resource methods that Groovy adds.

For example in some cases you will prefer to use a `Reader`, but still benefit from the automatic resource management from Groovy. In the next example, the reader will be closed even if the exception occurs:

    def count = 0, MAXSIZE = 3
    new File(baseDir,"haiku.txt").withReader { reader ->
        while (reader.readLine()) {
            if (++count > MAXSIZE) {
                throw new RuntimeException('Haiku should only have 3 verses')
            }
        }
    }

Should you need to collect the lines of a text file into a list, you can do:

    def list = new File(baseDir, 'haiku.txt').collect {it}

Or you can even leverage the `as` operator to get the contents of the file into an array of lines:

    def array = new File(baseDir, 'haiku.txt') as String[]

How many times did you have to get the contents of a file into a `byte[]` and how much code does it require? Groovy makes it very easy actually:

    byte[] contents = file.bytes

Groovy also provides asynchronous variants for reading and writing files via the `groovy-nio` module. These return a `CompletableFuture`, making them composable and easy to use with async frameworks.

    def path = file.toPath()
    def future = path.textAsync           // returns CompletableFuture<String>
    println future.get()                  // blocks until content is available

Similarly, `path.bytesAsync` returns a `CompletableFuture<byte[]>`, and `path.writeAsync(text)` writes content asynchronously.

Working with I/O is not limited to dealing with files. In fact, a lot of operations rely on input/output streams, hence why Groovy adds a lot of support methods to those, as you can see in the documentation.

As an example, you can obtain an `InputStream` from a `File` very easily:

    def is = new File(baseDir,'haiku.txt').newInputStream()
    // do something ...
    is.close()

However you can see that it requires you to deal with closing the inputstream. In Groovy it is in general a better idea to use the `withInputStream` idiom that will take care of that for you:

    new File(baseDir,'haiku.txt').withInputStream { stream ->
        // do something ...
    }

### 1.2. Writing files

Of course in some cases you won’t want to read but write a file. One of the options is to use a `Writer`:

    new File(baseDir,'haiku.txt').withWriter('utf-8') { writer ->
        writer.writeLine 'Into the ancient pond'
        writer.writeLine 'A frog jumps'
        writer.writeLine "Water's sound!"
    }

But for such a simple example, using the `<<` operator would have been enough:

    new File(baseDir,'haiku.txt') << '''Into the ancient pond
    A frog jumps
    Water's sound!'''

Of course we do not always deal with text contents, so you could use the `Writer` or directly write bytes as in this example:

    file.bytes = [66,22,11]

Of course you can also directly deal with output streams. For example, here is how you would create an output stream to write into a file:

    def os = new File(baseDir,'data.bin').newOutputStream()
    // do something ...
    os.close()

However you can see that it requires you to deal with closing the output stream. Again it is in general a better idea to use the `withOutputStream` idiom that will handle the exceptions and close the stream in any case:

    new File(baseDir,'data.bin').withOutputStream { stream ->
        // do something ...
    }

### 1.3. Traversing file trees

In scripting contexts it is a common task to traverse a file tree in order to find some specific files and do something with them. Groovy provides multiple methods to do this. For example you can perform something on all files of a directory:

    dir.eachFile { file ->                      (1)
        println file.name
    }
    dir.eachFileMatch(~/.*\.txt/) { file ->     (2)
        println file.name
    }

1 | executes the closure code on each file found in the directory
2 | executes the closure code on files in the directory matching the specified pattern

Often you will have to deal with a deeper hierarchy of files, in which case you can use `eachFileRecurse`:

    dir.eachFileRecurse { file ->                      (1)
        println file.name
    }

    dir.eachFileRecurse(FileType.FILES) { file ->      (2)
        println file.name
    }

1 | executes the closure code on each file or directory found in the directory, recursively
2 | executes the closure code only on files, but recursively

For more complex traversal techniques you can use the `traverse` method, which requires you to set a special flag indicating what to do with the traversal:

    dir.traverse { file ->
        if (file.directory && file.name=='bin') {
            FileVisitResult.TERMINATE                   (1)
        } else {
            println file.name
            FileVisitResult.CONTINUE                    (2)
        }

    }

1 | if the current file is a directory and its name is `bin`, stop the traversal
2 | otherwise print the file name and continue

### 1.4. Data and objects

In Java it is not uncommon to serialize and deserialize data using the `java.io.DataOutputStream` and `java.io.DataInputStream` classes respectively. Groovy will make it even easier to deal with them. For example, you could serialize data into a file and deserialize it using this code:

    boolean b = true
    String message = 'Hello from Groovy'
    // Serialize data into a file
    file.withDataOutputStream { out ->
        out.writeBoolean(b)
        out.writeUTF(message)
    }
    // ...
    // Then read it back
    file.withDataInputStream { input ->
        assert input.readBoolean() == b
        assert input.readUTF() == message
    }

And similarly, if the data you want to serialize implements the `Serializable` interface, you can proceed with an object output stream, as illustrated here:

    Person p = new Person(name:'Bob', age:76)
    // Serialize data into a file
    file.withObjectOutputStream { out ->
        out.writeObject(p)
    }
    // ...
    // Then read it back
    file.withObjectInputStream { input ->
        def p2 = input.readObject()
        assert p2.name == p.name
        assert p2.age == p.age
    }

### 1.5. Executing External Processes

The previous section described how easy it was to deal with files, readers or streams in Groovy. However in domains like system administration or devops it is often required to communicate with external processes.

Groovy provides `execute` and `toProcessBuilder` methods as two mechanisms to execute external processes. In simple cases, calls to `execute()` are invoked directly on a command string:

    "javac src/main/java/*.java".execute()

For more sophisticated workloads, a named-parameter variant configures the process with options like working directory, environment, and stream redirection:

    def result = "javac src/main/java/*.java".execute(
        dir: new File('/myproject'),
        redirectErrorStream: true
    ).waitForResult(2, TimeUnit.MINUTES)

    if (!result.ok) System.err.println result.out

The `toProcessBuilder()` method lets you piggyback directly onto the `ProcessBuilder` fluent API:

    def result = "javac src/main/java/*.java".toProcessBuilder()
        .directory(new File('/myproject'))
        .redirectErrorStream(true)
        .start()
        .waitForResult(2, TimeUnit.MINUTES)

    if (!result.ok) System.err.println result.out

The `execute()` and `start()` methods return a `java.lang.Process` instance, and `waitForResult()` returns a `ProcessResult` capturing stdout, stderr, and the exit code. The following sections cover each approach in detail.

#### 1.5.1. Simple execution

Simply write the command line as a string and call the `execute()` method. E.g., on a *nix machine (or a Windows machine with appropriate *nix commands installed), you can execute this:

    def process = "ls -l".execute()       (1)
    println "Files: $process.text"                 (2)

1 | executes the `ls` command in an external process
2 | consume the output of the command and retrieve the text

Here is the same command as above but we will now process the resulting stream a line at a time:

    def process = "ls -l".execute()             (1)
    process.in.eachLine { line ->               (2)
        println line                            (3)
    }

1 | executes the `ls` command in an external process
2 | for each line of the input stream of the process
3 | print the line

It is worth noting that `in` corresponds to an input stream to the standard output of the command. `out` will refer to a stream where you can send data to the process (its standard input).

Remember that many commands are shell built-ins and need special handling. So if you want a listing of files in a directory on a Windows machine and write:

    def process = "dir".execute()
    println "Files: $process.text"

you will receive an `IOException` saying Cannot run program "dir": CreateProcess error=2, The system cannot find the file specified.

This is because `dir` is built-in to the Windows shell (`cmd.exe`) and can’t be run as a simple executable. Instead, you will need to write:

    def process = "cmd /c dir".execute()
    println "Files: $process.text"

#### 1.5.2. Named parameters for execute

The `execute()` method accepts named parameters to configure the process. Supported options include `dir` (working directory), `env` (environment variables), `redirectErrorStream`, `inheritIO`, `outputFile`, `errorFile`, `inputFile`, `appendOutput`, and `appendError`. The file-related options (`dir`, `outputFile`, `errorFile`, `inputFile`, `appendOutput`, `appendError`) accept a `File`, `Path`, or `String`:

    def process = "ls".execute(dir: tmpDir)
    def result = process.waitForResult()
    assert result.ok
    assert result.out.contains('test.txt')

To merge stderr into stdout:

    def process = "ls /nonexistent_path_xxx".execute(redirectErrorStream: true)
    def output = process.text
    assert output.length() > 0  // error message now in stdout

#### 1.5.3. Using ProcessBuilder directly

For full control over process construction, the `toProcessBuilder()` method creates a `java.lang.ProcessBuilder` from a command string, array, or list:

    def pb = "echo Hello".toProcessBuilder()
    pb.redirectErrorStream(true)
    def process = pb.start()
    assert process.text.trim() == 'Hello'

#### 1.5.4. Capturing output, error, and exit code

A common need is to capture a process’s standard output, standard error, and exit code all at once. The `waitForResult()` method returns a `ProcessResult` with `out`, `err`, `exitCode`, and `ok` properties:

    def result = "echo Hello World".execute().waitForResult()
    assert result.ok
    assert result.out.trim() == 'Hello World'
    assert result.exitCode == 0

If the process produces an error:

    def result = "ls /nonexistent_path_xxx".execute().waitForResult()
    assert !result.ok
    assert result.err.length() > 0

A timeout variant is available that forcibly kills the process if it exceeds the given duration:

    def result = "echo Fast".execute().waitForResult(10, TimeUnit.SECONDS)
    assert result.ok
    assert result.out.trim() == 'Fast'

#### 1.5.5. Stream handling

Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, and even deadlock.

Because of this, Groovy provides some additional helper methods which make stream handling for processes easier.

Here is how to gobble all of the output (including the error stream output) from your process:

    def p = "rm -f foo.tmp".execute([], tmpDir)
    p.consumeProcessOutput()
    p.waitFor()

There are also variations of `consumeProcessOutput` that make use of `StringBuffer`, `InputStream`, `OutputStream` etc…​ For a complete list, please read the GDK API for java.lang.Process

#### 1.5.6. Piping

The `pipeTo` command (mapped to `|` to allow overloading) lets the output stream of one process be fed into the input stream of another process.

Here are some examples of use:

Pipes in action

    proc1 = 'ls'.execute()
    proc2 = 'tr -d o'.execute()
    proc3 = 'tr -d e'.execute()
    proc4 = 'tr -d i'.execute()
    proc1 | proc2 | proc3 | proc4
    proc4.waitFor()
    if (proc4.exitValue()) {
        println proc4.err.text
    } else {
        println proc4.text
    }

Consuming errors

    def sout = new StringBuilder()
    def serr = new StringBuilder()
    proc2 = 'tr -d o'.execute()
    proc3 = 'tr -d e'.execute()
    proc4 = 'tr -d i'.execute()
    proc4.consumeProcessOutput(sout, serr)
    proc2 | proc3 | proc4
    [proc2, proc3].each { it.consumeProcessErrorStream(serr) }
    proc2.withWriter { writer ->
        writer << 'testfile.groovy'
    }
    proc4.waitForOrKill(1000)
    println "Standard output: $sout"
    println "Standard error: $serr"

For more robust piping, the `pipeline()` method uses native OS-level piping via `ProcessBuilder.startPipeline()`:

    def procs = ["echo one two three", "wc -w"].pipeline()
    def result = procs.last().waitForResult()
    assert result.ok
    assert result.out.trim() == '3'

#### 1.5.7. Asynchronous notifications

The `onExit` method registers a closure to be called when the process terminates:

    def latch = new CountDownLatch(1)
    def exitCode = -1
    "echo done".execute().onExit { proc ->
        exitCode = proc.exitValue()
        latch.countDown()
    }
    latch.await(10, TimeUnit.SECONDS)
    assert exitCode == 0

## 2. Working with collections

Groovy provides native support for various collection types, including lists, maps or ranges. Most of those are based on the Java collection types and decorated with additional methods found in the Groovy development kit.

### 2.1. Lists

#### 2.1.1. List literals

You can create lists as follows. Notice that `[]` is the empty list expression.

    def list = [5, 6, 7, 8]
    assert list.get(2) == 7
    assert list[2] == 7
    assert list instanceof java.util.List

    def emptyList = []
    assert emptyList.size() == 0
    emptyList.add(5)
    assert emptyList.size() == 1

Each list expression creates an implementation of java.util.List.

Of course lists can be used as a source to construct another list:

    def list1 = ['a', 'b', 'c']
    //construct a new list, seeded with the same items as in list1
    def list2 = new ArrayList<String>(list1)

    assert list2 == list1 // == checks that each corresponding element is the same

    // clone() can also be called
    def list3 = list1.clone()
    assert list3 == list1

A list is an ordered collection of objects:

    def list = [5, 6, 7, 8]
    assert list.size() == 4
    assert list.getClass() == ArrayList     // the specific kind of list being used

    assert list[2] == 7                     // indexing starts at 0
    assert list.getAt(2) == 7               // equivalent method to subscript operator []
    assert list.get(2) == 7                 // alternative method

    list[2] = 9
    assert list == [5, 6, 9, 8,]           // trailing comma OK

    list.putAt(2, 10)                       // equivalent method to [] when value being changed
    assert list == [5, 6, 10, 8]
    assert list.set(2, 11) == 10            // alternative method that returns old value
    assert list == [5, 6, 11, 8]

    assert ['a', 1, 'a', 'a', 2.5, 2.5f, 2.5d, 'hello', 7g, null, 9 as byte]
    //objects can be of different types; duplicates allowed

    assert [1, 2, 3, 4, 5][-1] == 5             // use negative indices to count from the end
    assert [1, 2, 3, 4, 5][-2] == 4
    assert [1, 2, 3, 4, 5].getAt(-2) == 4       // getAt() available with negative index...
    try {
        [1, 2, 3, 4, 5].get(-2)                 // but negative index not allowed with get()
        assert false
    } catch (e) {
        assert e instanceof IndexOutOfBoundsException
    }

#### 2.1.2. List as a boolean expression

Lists can be evaluated as a `boolean` value:

    assert ![]             // an empty list evaluates as false

    //all other lists, irrespective of contents, evaluate as true
    assert [1] && ['a'] && [0] && [0.0] && [false] && [null]

#### 2.1.3. Iterating on a list

Iterating on elements of a list is usually done calling the `each` and `eachWithIndex` methods, which execute code on each item of a list:

    [1, 2, 3].each {
        println "Item: $it" // `it` is an implicit parameter corresponding to the current element
    }
    ['a', 'b', 'c'].eachWithIndex { it, i -> // `it` is the current element, while `i` is the index
        println "$i: $it"
    }

In addition to iterating, it is often useful to create a new list by transforming each of its elements into something else. This operation, often called mapping, is done in Groovy thanks to the `collect` method:

    assert [1, 2, 3].collect { it * 2 } == [2, 4, 6]

    // shortcut syntax instead of collect
    assert [1, 2, 3]*.multiply(2) == [1, 2, 3].collect { it.multiply(2) }

    def list = [0]
    // it is possible to give `collect` the list which collects the elements
    assert [1, 2, 3].collect(list) { it * 2 } == [0, 2, 4, 6]
    assert list == [0, 2, 4, 6]

When each element maps to a collection of results, `collectMany` transforms and flattens in a single step. It is the idiomatic (and more efficient) alternative to `collect { …​ }.flatten()`:

    def words = ['a', 'bb', 'ccc']
    // collectMany transforms each element into a collection, then flattens one level;
    // it is the idiomatic (and more efficient) alternative to collect {}.flatten()
    assert words.collectMany { [it, it.size()] } == ['a', 1, 'bb', 2, 'ccc', 3]

    // handy for expanding each element into zero, one or many results
    assert [1, 2, 3].collectMany { it % 2 == 0 ? [it, it] : [] } == [2, 2]

To build a `Map` from a list, `collectEntries` collects a key/value pair for each element:

    def languages = ['Groovy', 'Java', 'Kotlin']
    // collectEntries builds a map from a list; the closure returns a [key, value] pair per element
    assert languages.collectEntries { [it, it.size()] } == [Groovy: 6, Java: 4, Kotlin: 6]

    // a map literal can be returned instead of a two-element list
    assert languages.collectEntries { [(it): it.toUpperCase()] } ==
            [Groovy: 'GROOVY', Java: 'JAVA', Kotlin: 'KOTLIN']

When you need each element together with its position, `withIndex` provides a more composable alternative to `eachWithIndex`, while `indexed` returns a map from index to element:

    def vowels = ['a', 'e', 'i', 'o', 'u']

    // withIndex pairs each element with its index (starting at 0 by default)
    assert vowels.withIndex() == [['a', 0], ['e', 1], ['i', 2], ['o', 3], ['u', 4]]

    // an optional offset sets the starting index
    assert vowels.withIndex(1) == [['a', 1], ['e', 2], ['i', 3], ['o', 4], ['u', 5]]

    // often chained with collect for a cleaner alternative to eachWithIndex
    assert vowels.withIndex().collect { v, i -> "$i:$v" } == ['0:a', '1:e', '2:i', '3:o', '4:u']

    // indexed returns a map from index to element
    assert vowels.indexed(1) == [1: 'a', 2: 'e', 3: 'i', 4: 'o', 5: 'u']

#### 2.1.4. Manipulating lists

##### Filtering and searching

The Groovy development kit contains a lot of methods on collections that enhance the standard collections with pragmatic methods, some of which are illustrated here:

    assert [1, 2, 3].find { it > 1 } == 2           // find 1st element matching criteria
    assert [1, 2, 3].findAll { it > 1 } == [2, 3]   // find all elements matching critieria
    assert ['a', 'b', 'c', 'd', 'e'].findIndexOf {      // find index of 1st element matching criteria
        it in ['c', 'e', 'g']
    } == 2

    assert ['a', 'b', 'c', 'd', 'c'].indexOf('c') == 2  // index returned
    assert ['a', 'b', 'c', 'd', 'c'].indexOf('z') == -1 // index -1 means value not in list
    assert ['a', 'b', 'c', 'd', 'c'].lastIndexOf('c') == 4

    assert [1, 2, 3].every { it < 5 }               // returns true if all elements match the predicate
    assert ![1, 2, 3].every { it < 3 }
    assert [1, 2, 3].any { it > 2 }                 // returns true if any element matches the predicate
    assert ![1, 2, 3].any { it > 3 }

    assert [1, 2, 3, 4, 5, 6].sum() == 21                // sum anything with a plus() method
    assert ['a', 'b', 'c', 'd', 'e'].sum {
        it == 'a' ? 1 : it == 'b' ? 2 : it == 'c' ? 3 : it == 'd' ? 4 : it == 'e' ? 5 : 0
        // custom value to use in sum
    } == 15
    assert ['a', 'b', 'c', 'd', 'e'].sum { ((char) it) - ((char) 'a') } == 10
    assert ['a', 'b', 'c', 'd', 'e'].sum() == 'abcde'
    assert [['a', 'b'], ['c', 'd']].sum() == ['a', 'b', 'c', 'd']

    // an initial value can be provided
    assert [].sum(1000) == 1000
    assert [1, 2, 3].sum(1000) == 1006

    assert [1, 2, 3].join('-') == '1-2-3'           // String joining
    assert [1, 2, 3].inject('counting: ') {
        str, item -> str + item                     // reduce operation
    } == 'counting: 123'
    assert [1, 2, 3].inject(0) { count, item ->
        count + item
    } == 6

And here is idiomatic Groovy code for finding the maximum and minimum in a collection:

    def list = [9, 4, 2, 10, 5]
    assert list.max() == 10
    assert list.min() == 2

    // we can also compare single characters, as anything comparable
    assert ['x', 'y', 'a', 'z'].min() == 'a'

    // we can use a closure to specify the sorting behaviour
    def list2 = ['abc', 'z', 'xyzuvw', 'Hello', '321']
    assert list2.max { it.size() } == 'xyzuvw'
    assert list2.min { it.size() } == 'z'

In addition to closures, you can use a `Comparator` to define the comparison criteria:

    Comparator mc = { a, b -> a == b ? 0 : (a < b ? -1 : 1) }

    def list = [7, 4, 9, -6, -1, 11, 2, 3, -9, 5, -13]
    assert list.max(mc) == 11
    assert list.min(mc) == -13

    Comparator mc2 = { a, b -> a == b ? 0 : (Math.abs(a) < Math.abs(b)) ? -1 : 1 }

    assert list.max(mc2) == -13
    assert list.min(mc2) == -1

    assert list.max { a, b -> a.equals(b) ? 0 : Math.abs(a) < Math.abs(b) ? -1 : 1 } == -13
    assert list.min { a, b -> a.equals(b) ? 0 : Math.abs(a) < Math.abs(b) ? -1 : 1 } == -1

##### Adding or removing elements

We can use `[]` to assign a new empty list and `<<` to append items to it:

    def list = []
    assert list.empty

    list << 5
    assert list.size() == 1

    list << 7 << 'i' << 11
    assert list == [5, 7, 'i', 11]

    list << ['m', 'o']
    assert list == [5, 7, 'i', 11, ['m', 'o']]

    //first item in chain of << is target list
    assert ([1, 2] << 3 << [4, 5] << 6) == [1, 2, 3, [4, 5], 6]

    //using leftShift is equivalent to using <<
    assert ([1, 2, 3] << 4) == ([1, 2, 3].leftShift(4))

We can add to a list in many ways:

    assert [1, 2] + 3 + [4, 5] + 6 == [1, 2, 3, 4, 5, 6]
    // equivalent to calling the `plus` method
    assert [1, 2].plus(3).plus([4, 5]).plus(6) == [1, 2, 3, 4, 5, 6]

    def a = [1, 2, 3]
    a += 4      // creates a new list and assigns it to `a`
    a += [5, 6]
    assert a == [1, 2, 3, 4, 5, 6]

    assert [1, *[222, 333], 456] == [1, 222, 333, 456]
    assert [*[1, 2, 3]] == [1, 2, 3]
    assert [1, [2, 3, [4, 5], 6], 7, [8, 9]].flatten() == [1, 2, 3, 4, 5, 6, 7, 8, 9]

    def list = [1, 2]
    list.add(3)
    list.addAll([5, 4])
    assert list == [1, 2, 3, 5, 4]

    list = [1, 2]
    list.add(1, 3) // add 3 just before index 1
    assert list == [1, 3, 2]

    list.addAll(2, [5, 4]) //add [5,4] just before index 2
    assert list == [1, 3, 5, 4, 2]

    list = ['a', 'b', 'z', 'e', 'u', 'v', 'g']
    list[8] = 'x' // the [] operator is growing the list as needed
    // nulls inserted if required
    assert list == ['a', 'b', 'z', 'e', 'u', 'v', 'g', null, 'x']

It is however important that the `+` operator on a list is not mutating. Compared to `<<`, it will create a new list, which is often not what you want and can lead to performance issues.

The Groovy development kit also contains methods allowing you to easily remove elements from a list by value:

    assert ['a','b','c','b','b'] - 'c' == ['a','b','b','b']
    assert ['a','b','c','b','b'] - 'b' == ['a','c']
    assert ['a','b','c','b','b'] - ['b','c'] == ['a']

    def list = [1,2,3,4,3,2,1]
    list -= 3           // creates a new list by removing `3` from the original one
    assert list == [1,2,4,2,1]
    assert ( list -= [2,4] ) == [1,1]

It is also possible to remove an element by passing its index to the `remove` method, in which case the list is mutated:

    def list = ['a','b','c','d','e','f','b','b','a']
    assert list.remove(2) == 'c'        // remove the third element, and return it
    assert list == ['a','b','d','e','f','b','b','a']

In case you only want to remove the first element having the same value in a list, instead of removing all elements, you can call the `remove` method passing the value:

    def list= ['a','b','c','b','b']
    assert list.remove('c')             // remove 'c', and return true because element removed
    assert list.remove('b')             // remove first 'b', and return true because element removed

    assert ! list.remove('z')           // return false because no elements removed
    assert list == ['a','b','b']

As you can see, there are two `remove` methods available. One that takes an integer and removes an element by its index, and another that will remove the first element that matches the passed value. So what should we do when we have a list of integers? In this case, you may wish to use `removeAt` to remove an element by its index, and `removeElement` to remove the first element that matches a value.

    def list = [1,2,3,4,5,6,2,2,1]

    assert list.remove(2) == 3          // this removes the element at index 2, and returns it
    assert list == [1,2,4,5,6,2,2,1]

    assert list.removeElement(2)        // remove first 2 and return true
    assert list == [1,4,5,6,2,2,1]

    assert ! list.removeElement(8)      // return false because 8 is not in the list
    assert list == [1,4,5,6,2,2,1]

    assert list.removeAt(1) == 4        // remove element at index 1, and return it
    assert list == [1,5,6,2,2,1]

Of course, `removeAt` and `removeElement` will work with lists of any type.

Additionally, removing all the elements in a list can be done by calling the `clear` method:

    def list= ['a',2,'c',4]
    list.clear()
    assert list == []

##### Set operations

The Groovy development kit also includes methods making it easy to reason on sets:

    assert 'a' in ['a','b','c']             // returns true if an element belongs to the list
    assert ['a','b','c'].contains('a')      // equivalent to the `contains` method in Java
    assert [1,3,4].containsAll([1,4])       // `containsAll` will check that all elements are found

    assert [1,2,3,3,3,3,4,5].count(3) == 4  // count the number of elements which have some value
    assert [1,2,3,3,3,3,4,5].count {
        it%2==0                             // count the number of elements which match the predicate
    } == 2

    assert [1,2,4,6,8,10,12].intersect([1,3,6,9,12]) == [1,6,12]

    assert [1,2,3].disjoint( [4,6,9] )
    assert ![1,2,3].disjoint( [2,4,6] )

##### Sorting

Working with collections often implies sorting. Groovy offers a variety of options to sort lists, from using closures to comparators, as in the following examples:

    assert [6, 3, 9, 2, 7, 1, 5].sort() == [1, 2, 3, 5, 6, 7, 9]

    def list = ['abc', 'z', 'xyzuvw', 'Hello', '321']
    assert list.sort {
        it.size()
    } == ['z', 'abc', '321', 'Hello', 'xyzuvw']

    def list2 = [7, 4, -6, -1, 11, 2, 3, -9, 5, -13]
    assert list2.sort { a, b -> a == b ? 0 : Math.abs(a) < Math.abs(b) ? -1 : 1 } ==
            [-1, 2, 3, 4, 5, -6, 7, -9, 11, -13]

    Comparator mc = { a, b -> a == b ? 0 : Math.abs(a) < Math.abs(b) ? -1 : 1 }

    // JDK 8+ only
    // list2.sort(mc)
    // assert list2 == [-1, 2, 3, 4, 5, -6, 7, -9, 11, -13]

    def list3 = [6, -3, 9, 2, -7, 1, 5]

    Collections.sort(list3)
    assert list3 == [-7, -3, 1, 2, 5, 6, 9]

    Collections.sort(list3, mc)
    assert list3 == [1, 2, -3, 5, 6, -7, 9]

##### Removing duplicates

The `unique` method removes duplicate elements in place, while `toUnique` returns a new list, leaving the original untouched. Both accept an optional closure or comparator to define what "the same" means:

    // unique removes duplicates IN PLACE and returns the (now mutated) list
    def list = [3, 2, 1, 3, 3, 2]
    assert list.unique() == [3, 2, 1]
    assert list == [3, 2, 1]                            // the original list was mutated

    // toUnique returns a NEW list, leaving the original untouched
    def fruits = ['apple', 'apricot', 'banana', 'blueberry']
    assert fruits.toUnique { it[0] } == ['apple', 'banana']  // uniqueness by first letter
    assert fruits.size() == 4                           // original list unchanged

##### Taking and dropping elements

Groovy can select or discard elements from either end of a list, either by count (`take`, `drop`, `takeRight`, `dropRight`) or by predicate (`takeWhile`, `dropWhile`). None of these methods mutate the original list:

    def list = [1, 2, 3, 4, 5]

    // take/drop select or discard a number of leading elements
    assert list.take(2) == [1, 2]
    assert list.drop(2) == [3, 4, 5]

    // takeRight/dropRight work from the end of the list
    assert list.takeRight(2) == [4, 5]
    assert list.dropRight(2) == [1, 2, 3]

    // takeWhile/dropWhile use a predicate instead of a count, stopping at the first mismatch
    assert list.takeWhile { it < 3 } == [1, 2]
    assert list.dropWhile { it < 3 } == [3, 4, 5]

    assert list == [1, 2, 3, 4, 5]                      // none of the above mutate the original

##### Duplicating elements

The Groovy development kit also takes advantage of operator overloading to provide methods allowing duplication of elements of a list:

    assert [1, 2, 3] * 3 == [1, 2, 3, 1, 2, 3, 1, 2, 3]
    assert [1, 2, 3].multiply(2) == [1, 2, 3, 1, 2, 3]
    assert Collections.nCopies(3, 'b') == ['b', 'b', 'b']

    // nCopies from the JDK has different semantics than multiply for lists
    assert Collections.nCopies(2, [1, 2]) == [[1, 2], [1, 2]] //not [1,2,1,2]

### 2.2. Maps

#### 2.2.1. Map literals

In Groovy, maps (also known as associative arrays) can be created using the map literal syntax: `[:]`:

    def map = [name: 'Gromit', likes: 'cheese', id: 1234]
    assert map.get('name') == 'Gromit'
    assert map.get('id') == 1234
    assert map['name'] == 'Gromit'
    assert map['id'] == 1234
    assert map instanceof java.util.Map

    def emptyMap = [:]
    assert emptyMap.size() == 0
    emptyMap.put("foo", 5)
    assert emptyMap.size() == 1
    assert emptyMap.get("foo") == 5

Map keys are strings by default: `[a:1]` is equivalent to `['a':1]`. This can be confusing if you define a variable named `a` and that you want the value of `a` to be the key in your map. If this is the case, then you must escape the key by adding parenthesis, like in the following example:

    def a = 'Bob'
    def ages = [a: 43]
    assert ages['Bob'] == null // `Bob` is not found
    assert ages['a'] == 43     // because `a` is a literal!

    ages = [(a): 43]            // now we escape `a` by using parenthesis
    assert ages['Bob'] == 43   // and the value is found!

In addition to map literals, it is possible, to get a new copy of a map, to clone it:

    def map = [
            simple : 123,
            complex: [a: 1, b: 2]
    ]
    def map2 = map.clone()
    assert map2.get('simple') == map.get('simple')
    assert map2.get('complex') == map.get('complex')
    map2.get('complex').put('c', 3)
    assert map.get('complex').get('c') == 3

The resulting map is a shallow copy of the original one, as illustrated in the previous example.

#### 2.2.2. Map property notation

Maps also act like beans so you can use the property notation to get/set items inside the `Map` as long as the keys are strings which are valid Groovy identifiers:

    def map = [name: 'Gromit', likes: 'cheese', id: 1234]
    assert map.name == 'Gromit'     // can be used instead of map.get('name')
    assert map.id == 1234

    def emptyMap = [:]
    assert emptyMap.size() == 0
    emptyMap.foo = 5
    assert emptyMap.size() == 1
    assert emptyMap.foo == 5

Note: by design `map.foo` will always look for the key `foo` in the map. This means `foo.class` will return `null` on a map that doesn’t contain the `class` key. Should you really want to know the class, then you must use `getClass()`:

    def map = [name: 'Gromit', likes: 'cheese', id: 1234]
    assert map.class == null
    assert map.get('class') == null
    assert map.getClass() == LinkedHashMap // this is probably what you want

    map = [1      : 'a',
           (true) : 'p',
           (false): 'q',
           (null) : 'x',
           'null' : 'z']
    assert map.containsKey(1) // 1 is not an identifier so used as is
    assert map.true == null
    assert map.false == null
    assert map.get(true) == 'p'
    assert map.get(false) == 'q'
    assert map.null == 'z'
    assert map.get(null) == 'x'

##### Property access versus subscript and `get`

Because `map.k` reads the key `k` (rather than a bean property), property access, the subscript operator and `get` can diverge once a key collides with a real property. Every cell in the tables below is included from a passing assertion in `WorkingWithCollectionsTest`, so they always reflect the current runtime.

For a plain map there are only entries, so all four forms agree:

Table 1. Plain map (`[foo:'bar']`, `[class:'C']`, `[:]`)
key | `m.k` | `m.k` (`@CompileStatic`) | `m['k']` | `m.get('k')`
--- | --- | --- | --- | ---
ordinary (`'foo'`) | 'bar' | 'bar' | 'bar' | 'bar'
meta-name present (`'class'`) | 'C' | 'C' | 'C' | 'C'
meta-name absent (`'class'`) | null | null | null | null

A map subclass can declare real properties. Then `m.k` reads the property (identically under dynamic and `@CompileStatic` compilation), while `m['k']` and `m.get('k')` read the entry:

    static class MapWithProps extends HashMap<String, Object> {
        def foo = 1
        def bar = 2
    }

then, after `m.put('foo', 11)` and `m.put('baz', 33)`:

Table 2. Map subclass with declared properties `foo`, `bar`
key | `m.k` | `m.k` (`@CompileStatic`) | `m['k']` | `m.get('k')`
--- | --- | --- | --- | ---
`'foo'` (property=1, entry=11) | 1 | 1 | 11 | 11
`'bar'` (property=2, no entry) | 2 | 2 | null | null
`'baz'` (no property, entry=33) | 33 | 33 | 33 | 33

Two things to take away: after the addition of `getAt(Map, String)`/`putAt(Map, String, V)`, `m['k']` is equivalent to `m.get('k')` in every row and both compilation modes; and `m.k` resolves to a declared property when one exists (use `['k']`/`get` to be sure of reading the entry).

The tables above all use an explicit receiver `m`. A separate gotcha applies to an unqualified name read inside the body of a `Map` subclass — a method, a `with(map) { …​ }` block, or an anonymous-inner-class initializer. There the implicit receiver is the map itself, so the name is a property read on `this` and resolves to the entry, not to any binding in an enclosing scope. From Groovy 5, this self property takes precedence over an enclosing-class field of the same name (see GROOVY-10985), so such a reference silently yields `null` for a missing entry rather than reading the field:

    class Outer {
        static final String NAME = 'from-enclosing-field'

        // An unqualified name in the body of a Map subclass is a property read on
        // `this`, so it reads the (missing) entry and shadows the enclosing field:
        static Map<String, String> shadowed  = new HashMap<String, String>() { { put('k', NAME)       } }

        // Qualifying the reference resolves the enclosing field:
        static Map<String, String> qualified = new HashMap<String, String>() { { put('k', Outer.NAME) } }
    }

    assert Outer.shadowed['k']  == null                     // 'NAME' read as a (missing) entry
    assert Outer.qualified['k'] == 'from-enclosing-field'   // the enclosing field

Qualify the outer reference (`Outer.NAME` above) to read the enclosing field.

#### 2.2.3. Iterating on maps

As usual in the Groovy development kit, idiomatic iteration on maps makes use of the `each` and `eachWithIndex` methods. It’s worth noting that maps created using the map literal notation are ordered, that is to say that if you iterate on map entries, it is guaranteed that the entries will be returned in the same order they were added in the map.

    def map = [
            Bob  : 42,
            Alice: 54,
            Max  : 33
    ]

    // `entry` is a map entry
    map.each { entry ->
        println "Name: $entry.key Age: $entry.value"
    }

    // `entry` is a map entry, `i` the index in the map
    map.eachWithIndex { entry, i ->
        println "$i - Name: $entry.key Age: $entry.value"
    }

    // Alternatively you can use key and value directly
    map.each { key, value ->
        println "Name: $key Age: $value"
    }

    // Key, value and i as the index in the map
    map.eachWithIndex { key, value, i ->
        println "$i - Name: $key Age: $value"
    }

#### 2.2.4. Manipulating maps

##### Adding or removing elements

Adding an element to a map can be done either using the `put` method, the subscript operator or using `putAll`:

    def defaults = [1: 'a', 2: 'b', 3: 'c', 4: 'd']
    def overrides = [2: 'z', 5: 'x', 13: 'x']

    def result = new LinkedHashMap(defaults)
    result.put(15, 't')
    result[17] = 'u'
    result.putAll(overrides)
    assert result == [1: 'a', 2: 'z', 3: 'c', 4: 'd', 5: 'x', 13: 'x', 15: 't', 17: 'u']

Removing all the elements of a map can be done by calling the `clear` method:

    def m = [1:'a', 2:'b']
    assert m.get(1) == 'a'
    m.clear()
    assert m == [:]

Maps generated using the map literal syntax are using the object `equals` and `hashcode` methods. This means that you should never use an object which hash code is subject to change over time, or you wouldn’t be able to get the associated value back.

It is also worth noting that you should never use a `GString` as the key of a map, because the hash code of a `GString` is not the same as the hash code of an equivalent `String`:

    def key = 'some key'
    def map = [:]
    def gstringKey = "${key.toUpperCase()}"
    map.put(gstringKey,'value')
    assert map.get('SOME KEY') == null

##### Keys, values and entries

We can inspect the keys, values, and entries in a view:

    def map = [1:'a', 2:'b', 3:'c']

    def entries = map.entrySet()
    entries.each { entry ->
      assert entry.key in [1,2,3]
      assert entry.value in ['a','b','c']
    }

    def keys = map.keySet()
    assert keys == [1,2,3] as Set

Mutating values returned by the view (be it a map entry, a key or a value) is highly discouraged because success of the operation directly depends on the type of the map being manipulated. In particular, Groovy relies on collections from the JDK that in general make no guarantee that a collection can safely be manipulated through `keySet`, `entrySet`, or `values`.

##### Filtering and searching

The Groovy development kit contains filtering, searching and collecting methods similar to those found for lists:

    def people = [
        1: [name:'Bob', age: 32, gender: 'M'],
        2: [name:'Johnny', age: 36, gender: 'M'],
        3: [name:'Claire', age: 21, gender: 'F'],
        4: [name:'Amy', age: 54, gender:'F']
    ]

    def bob = people.find { it.value.name == 'Bob' } // find a single entry
    def females = people.findAll { it.value.gender == 'F' }

    // both return entries, but you can use collect to retrieve the ages for example
    def ageOfBob = bob.value.age
    def agesOfFemales = females.collect {
        it.value.age
    }

    assert ageOfBob == 32
    assert agesOfFemales == [21,54]

    // but you could also use a key/pair value as the parameters of the closures
    def agesOfMales = people.findAll { id, person ->
        person.gender == 'M'
    }.collect { id, person ->
        person.age
    }
    assert agesOfMales == [32, 36]

    // `every` returns true if all entries match the predicate
    assert people.every { id, person ->
        person.age > 18
    }

    // `any` returns true if any entry matches the predicate

    assert people.any { id, person ->
        person.age == 54
    }

##### Grouping

We can group a list into a map using some criteria:

    assert ['a', 7, 'b', [2, 3]].groupBy {
        it.class
    } == [(String)   : ['a', 'b'],
          (Integer)  : [7],
          (ArrayList): [[2, 3]]
    ]

    assert [
            [name: 'Clark', city: 'London'], [name: 'Sharma', city: 'London'],
            [name: 'Maradona', city: 'LA'], [name: 'Zhang', city: 'HK'],
            [name: 'Ali', city: 'HK'], [name: 'Liu', city: 'HK'],
    ].groupBy { it.city } == [
            London: [[name: 'Clark', city: 'London'],
                     [name: 'Sharma', city: 'London']],
            LA    : [[name: 'Maradona', city: 'LA']],
            HK    : [[name: 'Zhang', city: 'HK'],
                     [name: 'Ali', city: 'HK'],
                     [name: 'Liu', city: 'HK']],
    ]

##### Maps with a default value

The `withDefault` method wraps a map so that reading a missing key returns (and stores) a value computed by a closure. This is particularly handy for accumulating into per-key collections without having to pre-populate them:

    // withDefault wraps a map so that missing keys are computed on demand
    def wordsByLetter = [:].withDefault { [] }
    ['apple', 'banana', 'avocado', 'cherry'].each { word ->
        wordsByLetter[word[0]] << word          // no need to pre-populate the per-letter lists
    }
    assert wordsByLetter == [a: ['apple', 'avocado'], b: ['banana'], c: ['cherry']]

    // reading an absent key returns (and stores) the computed default
    def counts = [:].withDefault { 0 }
    assert counts['missing'] == 0

| Since Groovy 6, under static compilation, calling `withDefault` on a map with statically-known key/value types automatically selects the type-checked variant: the wrapped map rejects ill-typed keys and values at runtime instead of silently growing entries that violate the map’s declared generics (GROOVY-11807). Code that (usually inadvertently) relied on the lenient behaviour can defer the upgrade with the `groovy.classtag.preemption.disable` system property — see the `@ClassTag` documentation for details. Dynamic code is unaffected.

### 2.3. Ranges

Ranges allow you to create a list of sequential values. These can be used as `List` since Range extends java.util.List.

Ranges defined with the `..` notation are inclusive (that is the list contains the from and to value).

Ranges defined with the `..<` notation are half-open, they include the first value but not the last value.

Ranges defined with the `<..` notation are also half-open, they include the last value but not the first value.

Ranges defined with the `<..<` notation are full-open, they do not include the first value nor the last value.

    // an inclusive range
    def range = 5..8
    assert range.size() == 4
    assert range.get(2) == 7
    assert range[2] == 7
    assert range instanceof java.util.List
    assert range.contains(5)
    assert range.contains(8)

    // lets use a half-open range
    range = 5..<8
    assert range.size() == 3
    assert range.get(2) == 7
    assert range[2] == 7
    assert range instanceof java.util.List
    assert range.contains(5)
    assert !range.contains(8)

    //get the end points of the range without using indexes
    range = 1..10
    assert range.from == 1
    assert range.to == 10

Note that int ranges are implemented efficiently, creating a lightweight Java object containing a from and to value.

Ranges can be used for any Java object which implements java.lang.Comparable for comparison and also have methods `next()` and `previous()` to return the next / previous item in the range. For example, you can create a range of `String` elements:

    // an inclusive range
    def range = 'a'..'d'
    assert range.size() == 4
    assert range.get(2) == 'c'
    assert range[2] == 'c'
    assert range instanceof java.util.List
    assert range.contains('a')
    assert range.contains('d')
    assert !range.contains('e')

You can iterate on a range using a `for` loop:

    for (i in 1..10) {
        println "Hello ${i}"
    }

but alternatively you can achieve the same effect in a more Groovy idiomatic style, by iterating a range with `each` method:

    (1..10).each { i ->
        println "Hello ${i}"
    }

Ranges can be also used in the `switch` statement:

    switch (years) {
        case 1..10: interestRate = 0.076; break;
        case 11..25: interestRate = 0.052; break;
        default: interestRate = 0.037;
    }

### 2.4. Syntax enhancements for collections

#### 2.4.1. GPath support

Thanks to the support of property notation for both lists and maps, Groovy provides syntactic sugar making it really easy to deal with nested collections, as illustrated in the following examples:

    def listOfMaps = [['a': 11, 'b': 12], ['a': 21, 'b': 22]]
    assert listOfMaps.a == [11, 21] //GPath notation
    assert listOfMaps*.a == [11, 21] //spread dot notation

    listOfMaps = [['a': 11, 'b': 12], ['a': 21, 'b': 22], null]
    assert listOfMaps*.a == [11, 21, null] // caters for null values
    assert listOfMaps*.a == listOfMaps.collect { it?.a } //equivalent notation
    // But this will only collect non-null values
    assert listOfMaps.a == [11,21]

#### 2.4.2. Spread operator

The spread operator can be used to "inline" a collection into another. It is syntactic sugar which often avoids calls to `putAll` and facilitates the realization of one-liners:

    assert [ 'z': 900,
             *: ['a': 100, 'b': 200], 'a': 300] == ['a': 300, 'b': 200, 'z': 900]
    //spread map notation in map definition
    assert [*: [3: 3, *: [5: 5]], 7: 7] == [3: 3, 5: 5, 7: 7]

    def f = { [1: 'u', 2: 'v', 3: 'w'] }
    assert [*: f(), 10: 'zz'] == [1: 'u', 10: 'zz', 2: 'v', 3: 'w']
    //spread map notation in function arguments
    f = { map -> map.c }
    assert f(*: ['a': 10, 'b': 20, 'c': 30], 'e': 50) == 30

    f = { m, i, j, k -> [m, i, j, k] }
    //using spread map notation with mixed unnamed and named arguments
    assert f('e': 100, *[4, 5], *: ['a': 10, 'b': 20, 'c': 30], 6) ==
            [["e": 100, "b": 20, "c": 30, "a": 10], 4, 5, 6]

#### 2.4.3. The star-dot `*.' operator

The "star-dot" operator is a shortcut operator allowing you to call a method or a property on all elements of a collection:

    assert [1, 3, 5] == ['a', 'few', 'words']*.size()

    class Person {
        String name
        int age
    }
    def persons = [new Person(name:'Hugo', age:17), new Person(name:'Sandra',age:19)]
    assert [17, 19] == persons*.age

#### 2.4.4. Slicing with the subscript operator

You can index into lists, arrays, maps using the subscript expression. It is interesting that strings are considered as special kinds of collections in that context:

    def text = 'nice cheese gromit!'
    def x = text[2]

    assert x == 'c'
    assert x.class == String

    def sub = text[5..10]
    assert sub == 'cheese'

    def list = [10, 11, 12, 13]
    def answer = list[2,3]
    assert answer == [12,13]

Notice that you can use ranges to extract part of a collection:

    list = 100..200
    sub = list[1, 3, 20..25, 33]
    assert sub == [101, 103, 120, 121, 122, 123, 124, 125, 133]

The subscript operator can be used to update an existing collection (for collection type which are not immutable):

    list = ['a','x','x','d']
    list[1..2] = ['b','c']
    assert list == ['a','b','c','d']

It is worth noting that negative indices are allowed, to extract more easily from the end of a collection:

    text = "nice cheese gromit!"
    x = text[-1]
    assert x == "!"

You can use negative indices to count from the end of the List, array, String etc.

    def name = text[-7..-2]
    assert name == "gromit"

Eventually, if you use a backwards range (the starting index is greater than the end index), then the answer is reversed.

    text = "nice cheese gromit!"
    name = text[3..1]
    assert name == "eci"

### 2.5. Enhanced Collection Methods

In addition to lists, maps or ranges, Groovy offers a lot of additional methods for filtering, collecting, grouping, counting, …​ which are directly available on either collections or more easily iterables.

In particular, we invite you to read the Groovy development kit API docs and specifically:

- methods added to `Iterable` can be found here

- methods added to `Iterator` can be found here

- methods added to `Collection` can be found here

- methods added to `List` can be found here

- methods added to `Map` can be found here

## 3. Working with arrays

Groovy provides array support based on Java arrays with several extensions found in the Groovy development kit. The overall intention is that whether you are using an array or a collection, the code for working with the aggregate remains the same.

### 3.1. Arrays

#### 3.1.1. Array literals

You can create arrays as follows. Notice that `[]` is also used as the empty array expression when given an explicit array type.

    Number[] nums = [5, 6, 7, 8]
    assert nums[1] == 6
    assert nums.getAt(2) == 7                // alternative syntax
    assert nums[-1] == 8                     // negative indices
    assert nums instanceof Number[]

    int[] primes = [2, 3, 5, 7]              // primitives
    assert primes instanceof int[]

    def odds = [1, 3, 5] as int[]            // alt syntax 1
    assert odds instanceof int[]

    def evens = new int[]{2, 4, 6}           // alt syntax 2
    assert evens instanceof int[]

    // empty array examples
    Number[] emptyNums = []
    assert emptyNums instanceof Number[] && emptyNums.size() == 0

    var emptyObjects = new Object[0]         // alternative syntax 1
    assert emptyObjects instanceof Object[] && emptyObjects.size() == 0

    def emptyStrings = new String[]{}        // alternative syntax 2
    assert emptyStrings instanceof String[] && emptyStrings.size() == 0

    // multi-dimension examples
    int[][] manyInts = [ [1,2], [3,4] ]
    assert manyInts instanceof int[][] && manyInts.size() == 2
    assert manyInts[0].size() == 2 && manyInts[1].size() == 2

    def manyStrings = new String[][] { {'one','two'}, {'three','four'}, }
    assert manyStrings instanceof String[][] && manyStrings.size() == 2
    assert manyStrings[0].size() == 2 && manyStrings[1].size() == 2
    assert manyStrings[0][0] == 'one'
    assert manyStrings[-1][-1] == 'four'

#### 3.1.2. Iterating on an array

Iterating on elements of an array is usually done calling the `each` and `eachWithIndex` methods, which execute code on each item of an array:

    String[] vowels = ['a', 'e', 'i', 'o', 'u']
    var result = ''
    vowels.each {
        result += it
    }
    assert result == 'aeiou'
    result = ''
    vowels.eachWithIndex { v, i ->
        result += v * i         // index starts from 0
    }
    assert result == 'eiiooouuuu'

    result = ''
    int[] nums = [0, 1, 2]
    nums.eachWithIndex { value, index ->
        result += value.doubleValue()
    }
    assert result == '0.01.02.0'

#### 3.1.3. Other useful methods

There are numerous other GDK methods for working with arrays. Just be a little careful to read the documentation. For collections, there are some mutating methods which alter the original collection and others which produce new collections, leaving the original untouched. Since arrays are of a fixed size, we wouldn’t expect mutating methods which altered an array’s size. Often instead, such methods return collections. Here are some interesting array GDK methods:

    int[] nums = [1, 2, 3]
    def doubled = nums.collect { it * 2 }
    assert doubled == [2, 4, 6] && doubled instanceof List
    def tripled = nums*.multiply(3)
    assert tripled == [3, 6, 9] && doubled instanceof List

    assert nums.any{ it > 2 }
    assert nums.every{ it < 4 }
    assert nums.average() == 2
    assert nums.min() == 1
    assert nums.max() == 3
    assert nums.sum() == 6
    assert nums.indices == [0, 1, 2]
    assert nums.swap(0, 2) == [3, 2, 1] as int[]

    Integer[] moreNums = [10, 20, 30, 40]
    assert moreNums.find { it > 15 } == 20              // first matching element
    assert moreNums.findAll { it > 15 } == [20, 30, 40] // all matching elements (as a List)
    assert moreNums[1..2] == [20, 30]                   // slice an array with a range (returns a List)
    assert moreNums[-1] == 40                            // negative indices count from the end

    int[][] pairs = [[1, 2], [3, 4]]
    assert pairs.flatten() == [1, 2, 3, 4]              // flatten nested arrays into a single List

## 4. Working with legacy Date/Calendar types

The `groovy-dateutil` module supports numerous extensions for working with Java’s classic `Date` and `Calendar` classes.

You can access the properties of a `Date` or `Calendar` using the normal array index notation with the constant field numbers from the `Calendar` class as shown in the following example:

    import static java.util.Calendar.*    (1)

    def cal = Calendar.instance
    cal[YEAR] = 2000                      (2)
    cal[MONTH] = JANUARY                  (2)
    cal[DAY_OF_MONTH] = 1                 (2)
    assert cal[DAY_OF_WEEK] == SATURDAY   (3)

1 | Import the constants
2 | Setting the calendar’s year, month and day of month
3 | Accessing the calendar’s day of week

Groovy supports arithmetic on and iteration between `Date` and `Calendar` instances as shown in the following example:

    def utc = TimeZone.getTimeZone('UTC')
    Date date = Date.parse("yyyy-MM-dd HH:mm", "2010-05-23 09:01", utc)

    def prev = date - 1
    def next = date + 1

    def diffInDays = next - prev
    assert diffInDays == 2

    int count = 0
    prev.upto(next) { count++ }
    assert count == 3

You can parse strings into dates and output dates into formatted strings:

    def orig = '2000-01-01'
    def newYear = Date.parse('yyyy-MM-dd', orig)
    assert newYear[DAY_OF_WEEK] == SATURDAY
    assert newYear.format('yyyy-MM-dd') == orig
    assert newYear.format('dd/MM/yyyy') == '01/01/2000'

You can also create a new Date or Calendar based on an existing one:

    def newYear = Date.parse('yyyy-MM-dd', '2000-01-01')
    def newYearsEve = newYear.copyWith(
        year: 1999,
        month: DECEMBER,
        dayOfMonth: 31
    )
    assert newYearsEve[DAY_OF_WEEK] == FRIDAY

### 4.1. A number-based DSL for durations

The `org.apache.groovy.dateutil.TimeCategory` category adds properties to numbers for producing durations that can be added to or subtracted from `Date` instances:

    use (org.apache.groovy.dateutil.TimeCategory) {
        def christmas = new Date(124, 11, 25)     // 25 Dec 2024
        assert (christmas + 1.week - 3.days).date == 29
        assert (christmas - christmas).toMilliseconds() == 0

        def party = 2.hours + 30.minutes
        assert party.toMilliseconds() == (150 * 60 * 1000)
    }

This is a dequirked, `java.util.Date`-flavored parallel to the (now deprecated) `groovy.time.TimeCategory`. Relative to the legacy class, `.ago`/`.from.now` preserve the time-of-day uniformly (they no longer floor day/month/year amounts to midnight), and `toMilliseconds()` is deterministic (years and months use fixed `java.time.temporal.ChronoUnit` estimates rather than resolving against the current date). A `java.time` flavored equivalent, `org.apache.groovy.datetime.TimeCategory`, is available in the `groovy-datetime` module.

## 5. Working with Date/Time types

The `groovy-datetime` module supports numerous extensions for working with the Date/Time API introduced in Java 8. This documentation refers to the data types defined by this API as "JSR 310 types."

### 5.1. Formatting and parsing

A common use case when working with date/time types is to convert them to Strings (formatting) and from Strings (parsing). Groovy provides these additional formatting methods:

Method | Description | Example
--- | --- | ---
`getDateString()` | For `LocalDate` and `LocalDateTime`, formats with `DateTimeFormatter.ISO_LOCAL_DATE` | `2018-03-10`
| For `OffsetDateTime`, formats with `DateTimeFormatter.ISO_OFFSET_DATE` | `2018-03-10+04:00`
| For `ZonedDateTime`, formats with `DateTimeFormatter.ISO_LOCAL_DATE` and appends the `ZoneId` short name | `2018-03-10EST`
`getDateTimeString()` | For `LocalDateTime`, formats with `DateTimeFormatter.ISO_LOCAL_DATE_TIME` | `2018-03-10T20:30:45`
| For `OffsetDateTime`, formats with `DateTimeFormatter.ISO_OFFSET_DATE_TIME` | `2018-03-10T20:30:45+04:00`
| For `ZonedDateTime`, formats with `DateTimeFormatter.ISO_LOCAL_DATE_TIME` and appends the `ZoneId` short name | `2018-03-10T20:30:45EST`
`getTimeString()` | For `LocalTime` and `LocalDateTime`, formats with `DateTimeFormatter.ISO_LOCAL_TIME` | `20:30:45`
| For `OffsetTime` and `OffsetDateTime`, formats with `DateTimeFormatter.ISO_OFFSET_TIME` formatter | `20:30:45+04:00`
| For `ZonedDateTime`, formats with `DateTimeFormatter.ISO_LOCAL_TIME` and appends the `ZoneId` short name | `20:30:45EST`
`format(FormatStyle style)` | For `LocalTime` and `OffsetTime`, formats with `DateTimeFormatter.ofLocalizedTime(style)` | `4:30 AM` (with style `FormatStyle.SHORT`, e.g.)
| For `LocalDate`, formats with `DateTimeFormatter.ofLocalizedDate(style)` | `Saturday, March 10, 2018` (with style `FormatStyle.FULL`, e.g.)
| For `LocalDateTime`, `OffsetDateTime`, and `ZonedDateTime` formats with `DateTimeFormatter.ofLocalizedDateTime(style)` | `Mar 10, 2019 4:30:45 AM` (with style `FormatStyle.MEDIUM`, e.g.)
`format(String pattern)` | Formats with `DateTimeFormatter.ofPattern(pattern)` | `03/10/2018` (with pattern ’MM/dd/yyyy', e.g.)

For parsing, Groovy adds a static `parse` method to many of the JSR 310 types. The method takes two arguments: the value to be formatted and the pattern to use. The pattern is defined by the `java.time.format.DateTimeFormatter` API. As an example:

    def date = LocalDate.parse('Jun 3, 04', 'MMM d, yy')
    assert date == LocalDate.of(2004, Month.JUNE, 3)

    def time = LocalTime.parse('4:45', 'H:mm')
    assert time == LocalTime.of(4, 45, 0)

    def offsetTime = OffsetTime.parse('09:47:51-1234', 'HH:mm:ssZ')
    assert offsetTime == OffsetTime.of(9, 47, 51, 0, ZoneOffset.ofHoursMinutes(-12, -34))

    def dateTime = ZonedDateTime.parse('2017/07/11 9:47PM Pacific Standard Time', 'yyyy/MM/dd h:mma zzzz')
    assert dateTime == ZonedDateTime.of(
            LocalDate.of(2017, 7, 11),
            LocalTime.of(21, 47, 0),
            ZoneId.of('America/Los_Angeles')
    )

Note that these `parse` methods have a different argument ordering than the static `parse` method Groovy added to `java.util.Date`. This was done to be consistent with the existing `parse` methods of the Date/Time API.

### 5.2. Manipulating date/time

#### 5.2.1. Addition and subtraction

`Temporal` types have `plus` and `minus` methods for adding or subtracting a provided `java.time.temporal.TemporalAmount` argument. Because Groovy maps the `+` and `-` operators to single-argument methods of these names, a more natural expression syntax can be used to add and subtract.

    def aprilFools = LocalDate.of(2018, Month.APRIL, 1)

    def nextAprilFools = aprilFools + Period.ofDays(365) // add 365 days
    assert nextAprilFools.year == 2019

    def idesOfMarch = aprilFools - Period.ofDays(17) // subtract 17 days
    assert idesOfMarch.dayOfMonth == 15
    assert idesOfMarch.month == Month.MARCH

Groovy provides additional `plus` and `minus` methods that accept an integer argument, enabling the above to be rewritten more succinctly:

    def nextAprilFools = aprilFools + 365 // add 365 days
    def idesOfMarch = aprilFools - 17 // subtract 17 days

The unit of these integers depends on the JSR 310 type operand. As evident above, integers used with `ChronoLocalDate` types like `LocalDate` have a unit of days. Integers used with `Year` and `YearMonth` have a unit of years and months, respectively. All other types have a unit of seconds, such as `LocalTime`, for instance:

    def mars = LocalTime.of(12, 34, 56) // 12:34:56 pm

    def thirtySecondsToMars = mars - 30 // go back 30 seconds
    assert thirtySecondsToMars.second == 26

#### 5.2.2. Multiplication and division

The `*` operator can be used to multiply `Period` and `Duration` instances by an integer value; the `/` operator can be used to divide `Duration` instances by an integer value.

    def period = Period.ofMonths(1) * 2 // a 1-month period times 2
    assert period.months == 2

    def duration = Duration.ofSeconds(10) / 5// a 10-second duration divided by 5
    assert duration.seconds == 2

#### 5.2.3. Incrementing and decrementing

The `++` and `--` operators can be used increment and decrement date/time values by one unit. Since the JSR 310 types are immutable, the operation will create a new instance with the incremented/decremented value and reassign it to the reference.

    def year = Year.of(2000)
    --year // decrement by one year
    assert year.value == 1999

    def offsetTime = OffsetTime.of(0, 0, 0, 0, ZoneOffset.UTC) // 00:00:00.000 UTC
    offsetTime++ // increment by one second
    assert offsetTime.second == 1

#### 5.2.4. Negation

The `Duration` and `Period` types represent a negative or positive length of time. These can be negated with the unary `-` operator.

    def duration = Duration.ofSeconds(-15)
    def negated = -duration
    assert negated.seconds == 15

### 5.3. Interacting with date/time values

#### 5.3.1. Property notation

The `getLong(TemporalField)` method of `TemporalAccessor` types (e.g. `LocalDate`, `LocalTime`, `ZonedDateTime`, etc.) and the `get(TemporalUnit)` method of `TemporalAmount` types (namely `Period` and `Duration`), can be invoked with Groovy’s property notation. For example:

    def date = LocalDate.of(2018, Month.MARCH, 12)
    assert date[ChronoField.YEAR] == 2018
    assert date[ChronoField.MONTH_OF_YEAR] == Month.MARCH.value
    assert date[ChronoField.DAY_OF_MONTH] == 12
    assert date[ChronoField.DAY_OF_WEEK] == DayOfWeek.MONDAY.value

    def period = Period.ofYears(2).withMonths(4).withDays(6)
    assert period[ChronoUnit.YEARS] == 2
    assert period[ChronoUnit.MONTHS] == 4
    assert period[ChronoUnit.DAYS] == 6

#### 5.3.2. Ranges, `upto`, and `downto`

The JSR 310 types can be used with the range operator. The following example iterates between today and the `LocalDate` six days from now, printing out the day of the week for each iteration. As both range bounds are inclusive, this prints all seven days of the week.

    def start = LocalDate.now()
    def end = start + 6 // 6 days later
    (start..end).each { date ->
        println date.dayOfWeek
    }

The `upto` method will accomplish the same as the range in the above example. The `upto` method iterates from the earlier `start` value (inclusive) to the later `end` value (also inclusive), calling the closure with the incremented `next` value once per iteration.

    def start = LocalDate.now()
    def end = start + 6 // 6 days later
    start.upto(end) { next ->
        println next.dayOfWeek
    }

The `downto` method iterates in the opposite direction, from a later `start` value to an earlier `end` value.

The unit of iteration for `upto`, `downto`, and ranges is the same as the unit for addition and subtraction: `LocalDate` iterates by one day at a time, `YearMonth` iterates by one month, `Year` by one year, and everything else by one second. Both methods also support an optional a `TemporalUnit` argument to change the unit of iteration.

Consider the following example, where March 1st, 2018 is iterated up to March 2nd, 2018 using an iteration unit of months.

    def start = LocalDate.of(2018, Month.MARCH, 1)
    def end = start + 1 // 1 day later

    int iterationCount = 0
    start.upto(end, ChronoUnit.MONTHS) { next ->
        println next
        ++iterationCount
    }

    assert iterationCount == 1

Since the `start` date is inclusive, the closure is called with a `next` date value of March 1st. The `upto` method then increments the date by one month, yielding the date, April 1st. Because this date is after the specified `end` date of March 2nd, the iteration stops immediately, having only called the closure once. This behavior is the same for the `downto` method except that the iteration will stop as soon as the value of `next` becomes earlier than the targeted `end` date.

In short, when iterating with the `upto` or `downto` methods with a custom unit of iteration, the current value of iteration will never exceed the end value.

#### 5.3.3. Combining date/time values

The left-shift operator (`<<`) can be used to combine two JSR 310 types into an aggregate type. For example, a `LocalDate` can be left-shifted into a `LocalTime` to produce a composite `LocalDateTime` instance.

    MonthDay monthDay = Month.JUNE << 3 // June 3rd
    LocalDate date = monthDay << Year.of(2015) // 3-Jun-2015
    LocalDateTime dateTime = date << LocalTime.NOON // 3-Jun-2015 @ 12pm
    OffsetDateTime offsetDateTime = dateTime << ZoneOffset.ofHours(-5) // 3-Jun-2015 @ 12pm UTC-5

The left-shift operator is reflexive; the order of the operands does not matter.

    def year = Year.of(2000)
    def month = Month.DECEMBER

    YearMonth a = year << month
    YearMonth b = month << year
    assert a == b

#### 5.3.4. Creating periods and durations

The right-shift operator (`>>`) produces a value representing the period or duration between the operands. For `ChronoLocalDate`, `YearMonth`, and `Year`, the operator yields a `Period` instance:

    def newYears = LocalDate.of(2018, Month.JANUARY, 1)
    def aprilFools = LocalDate.of(2018, Month.APRIL, 1)

    def period = newYears >> aprilFools
    assert period instanceof Period
    assert period.months == 3

The operator produces a `Duration` for the time-aware JSR types:

    def duration = LocalTime.NOON >> (LocalTime.NOON + 30)
    assert duration instanceof Duration
    assert duration.seconds == 30

If the value on the left-hand side of the operator is earlier than the value on the right-hand side, the result is positive. If the left-hand side is later than the right-hand side, the result is negative:

    def decade = Year.of(2010) >> Year.of(2000)
    assert decade.years == -10

#### 5.3.5. A number-based DSL for periods and durations

The `org.apache.groovy.datetime.TimeCategory` category adds properties to numbers for producing `Duration` (time-based) and `Period` (date-based) values, giving a concise DSL for date/time manipulation:

    import java.time.LocalDate
    import java.time.LocalDateTime
    use (org.apache.groovy.datetime.TimeCategory) {
        assert 1.hour == java.time.Duration.ofHours(1)
        assert 2.months == java.time.Period.ofMonths(2)

        // apply amounts to temporals via their native arithmetic
        assert LocalDate.of(2000, 1, 1) + 2.months == LocalDate.of(2000, 3, 1)

        // date-based and time-based amounts compose by chaining
        def base = LocalDateTime.of(2000, 1, 1, 0, 0, 0)
        assert base + 1.month + 3.hours == LocalDateTime.of(2000, 2, 1, 3, 0, 0)

        // relative-time terminals: a Period yields a LocalDate, a Duration a LocalDateTime
        assert 2.days.ago instanceof LocalDate
        assert 1.hour.from.now instanceof LocalDateTime
    }

Time-based units (`nanoseconds`, `milliseconds`, `seconds`, `minutes`, `hours`) produce a `Duration`; date-based units (`days`, `weeks`, `months`, `years`) produce a `Period`. This is the modern counterpart to the (now deprecated) `groovy.time.TimeCategory`. A `java.util.Date` flavored equivalent, `org.apache.groovy.dateutil.TimeCategory`, is available in the `groovy-dateutil` module.

### 5.4. Converting between legacy and JSR 310 types

Despite the shortcomings of `Date`, `Calendar`, and `TimeZone` types in the `java.util` package they are fairly common in Java APIs (at least in those prior to Java 8). To accommodate use of such APIs, Groovy provides methods for converting between the JSR 310 types and legacy types.

Most JSR types have been fitted with `toDate()` and `toCalendar()` methods for converting to relatively equivalent `java.util.Date` and `java.util.Calendar` values. Both `ZoneId` and `ZoneOffset` have been given a `toTimeZone()` method for converting to `java.util.TimeZone`.

    // LocalDate to java.util.Date
    def valentines = LocalDate.of(2018, Month.FEBRUARY, 14)
    assert valentines.toDate().format('MMMM dd, yyyy') == 'February 14, 2018'

    // LocalTime to java.util.Date
    def noon = LocalTime.of(12, 0, 0)
    assert noon.toDate().format('HH:mm:ss') == '12:00:00'

    // ZoneId to java.util.TimeZone
    def newYork = ZoneId.of('America/New_York')
    assert newYork.toTimeZone() == TimeZone.getTimeZone('America/New_York')

    // ZonedDateTime to java.util.Calendar
    def valAtNoonInNY = ZonedDateTime.of(valentines, noon, newYork)
    assert valAtNoonInNY.toCalendar().getTimeZone().toZoneId() == newYork

Note that when converting to a legacy type:

- Nanosecond values are truncated to milliseconds. A `LocalTime`, for example, with a `ChronoUnit.NANOS` value of 999,999,999 nanoseconds translates to 999 milliseconds.

- When converting the "local" types (`LocalDate`, `LocalTime`, and `LocalDateTime`), the time zone of the returned `Date` or `Calendar` will be the system default.

- When converting a time-only type (`LocalTime` or `OffsetTime`), the year/month/day of the `Date` or `Calendar` is set to the current date.

- When converting a date-only type (`LocalDate`), the time value of the `Date` or `Calendar` will be cleared, i.e. `00:00:00.000`.

- When converting an `OffsetDateTime` to a `Calendar`, only the hours and minutes of the `ZoneOffset` convey into the corresponding `TimeZone`. Fortunately, Zone Offsets with non-zero seconds are rare.

Groovy has added a number of methods to `Date` and `Calendar` for converting into the various JSR 310 types:

    Date legacy = Date.parse('yyyy-MM-dd HH:mm:ss.SSS', '2010-04-03 10:30:58.999')

    assert legacy.toLocalDate() == LocalDate.of(2010, 4, 3)
    assert legacy.toLocalTime() == LocalTime.of(10, 30, 58, 999_000_000) // 999M ns = 999ms
    assert legacy.toOffsetTime().hour == 10
    assert legacy.toYear() == Year.of(2010)
    assert legacy.toMonth() == Month.APRIL
    assert legacy.toDayOfWeek() == DayOfWeek.SATURDAY
    assert legacy.toMonthDay() == MonthDay.of(Month.APRIL, 3)
    assert legacy.toYearMonth() == YearMonth.of(2010, Month.APRIL)
    assert legacy.toLocalDateTime().year == 2010
    assert legacy.toOffsetDateTime().dayOfMonth == 3
    assert legacy.toZonedDateTime().zone == ZoneId.systemDefault()

## 6. Handy utilities

### 6.1. ConfigSlurper

`ConfigSlurper` is a utility class for reading configuration files defined in the form of Groovy scripts. Like it is the case with Java `*.properties` files, `ConfigSlurper` allows a dot notation. But in addition, it allows for Closure scoped configuration values and arbitrary object types.

    def config = new ConfigSlurper().parse('''
        app.date = new Date()  (1)
        app.age  = 42
        app {                  (2)
            name = "Test${42}"
        }
    ''')

    assert config.app.date instanceof Date
    assert config.app.age == 42
    assert config.app.name == 'Test42'

1 | Usage of the dot notation
2 | Usage of Closure scopes as an alternative to the dot notation

As can be seen in the above example, the `parse` method can be used to retrieve `groovy.util.ConfigObject` instances. The `ConfigObject` is a specialized `java.util.Map` implementation that either returns the configured value or a new `ConfigObject` instance but never `null`.

    def config = new ConfigSlurper().parse('''
        app.date = new Date()
        app.age  = 42
        app.name = "Test${42}"
    ''')

    assert config.test != null   (1)

1 | `config.test` has not been specified yet it returns a `ConfigObject` when being called.

In the case of a dot being part of a configuration variable name, it can be escaped by using single or double quotes.

    def config = new ConfigSlurper().parse('''
        app."person.age"  = 42
    ''')

    assert config.app."person.age" == 42

In addition, `ConfigSlurper` comes with support for `environments`. The `environments` method can be used to hand over a Closure instance that itself may consist of a several sections. Let’s say we wanted to create a particular configuration value for the development environment. When creating the `ConfigSlurper` instance we can use the `ConfigSlurper(String)` constructor to specify the target environment.

    def config = new ConfigSlurper('development').parse('''
      environments {
           development {
               app.port = 8080
           }

           test {
               app.port = 8082
           }

           production {
               app.port = 80
           }
      }
    ''')

    assert config.app.port == 8080

| The `ConfigSlurper` environments aren’t restricted to any particular environment names. It solely depends on the `ConfigSlurper` client code what value are supported and interpreted accordingly.

The `environments` method is built-in but the `registerConditionalBlock` method can be used to register other method names in addition to the `environments` name.

    def slurper = new ConfigSlurper()
    slurper.registerConditionalBlock('myProject', 'developers')   (1)

    def config = slurper.parse('''
      sendMail = true

      myProject {
           developers {
               sendMail = false
           }
      }
    ''')

    assert !config.sendMail

1 | Once the new block is registered `ConfigSlurper` can parse it.

For Java integration purposes the `toProperties` method can be used to convert the `ConfigObject` to a `java.util.Properties` object that might be stored to a `*.properties` text file. Be aware though that the configuration values are converted to `String` instances during adding them to the newly created `Properties` instance.

    def config = new ConfigSlurper().parse('''
        app.date = new Date()
        app.age  = 42
        app {
            name = "Test${42}"
        }
    ''')

    def properties = config.toProperties()

    assert properties."app.date" instanceof String
    assert properties."app.age" == '42'
    assert properties."app.name" == 'Test42'

### 6.2. Expando

The `Expando` class can be used to create a dynamically expandable object. Despite its name it does not use the `ExpandoMetaClass` underneath. Each `Expando` object represents a standalone, dynamically-crafted instance that can be extended with properties (or methods) at runtime.

    def expando = new Expando()
    expando.name = 'John'

    assert expando.name == 'John'

A special case occurs when a dynamic property registers a `Closure` code block. Once being registered it can be invoked as it would be done with a method call.

    def expando = new Expando()
    expando.toString = { -> 'John' }
    expando.say = { String s -> "John says: ${s}" }

    assert expando as String == 'John'
    assert expando.say('Hi') == 'John says: Hi'

### 6.3. Observable list, map and set

Groovy comes with observable lists, maps and sets. Each of these collections trigger `java.beans.PropertyChangeEvent` events when elements are added, removed or changed. Note that a `PropertyChangeEvent` is not only signalling that a certain event has occurred, moreover, it holds information on the property name and the old/new value a certain property has been changed to.

Depending on the type of change that has happened, observable collections might fire more specialized `PropertyChangeEvent` types. For example, adding an element to an observable list fires an `ObservableList.ElementAddedEvent` event.

    def event                                       (1)
    def listener = {
        if (it instanceof ObservableList.ElementEvent)  {  (2)
            event = it
        }
    } as PropertyChangeListener

    def observable = [1, 2, 3] as ObservableList    (3)
    observable.addPropertyChangeListener(listener)  (4)

    observable.add 42                               (5)

    assert event instanceof ObservableList.ElementAddedEvent

    def elementAddedEvent = event as ObservableList.ElementAddedEvent
    assert elementAddedEvent.changeType == ObservableList.ChangeType.ADDED
    assert elementAddedEvent.index == 3
    assert elementAddedEvent.oldValue == null
    assert elementAddedEvent.newValue == 42

1 | Declares a `PropertyChangeEventListener` that is capturing the fired events
2 | `ObservableList.ElementEvent` and its descendant types are relevant for this listener
3 | Creates an `ObservableList` from the given list
4 | Registers the listener
5 | Triggers an `ObservableList.ElementAddedEvent` event

| Be aware that adding an element in fact causes two events to be triggered. The first is of type `ObservableList.ElementAddedEvent`, the second is a plain `PropertyChangeEvent` that informs listeners about the change of property `size`.

The `ObservableList.ElementClearedEvent` event type is another interesting one. Whenever multiple elements are removed, for example when calling `clear()`, it holds the elements being removed from the list.

    def event
    def listener = {
        if (it instanceof ObservableList.ElementEvent)  {
            event = it
        }
    } as PropertyChangeListener

    def observable = [1, 2, 3] as ObservableList
    observable.addPropertyChangeListener(listener)

    observable.clear()

    assert event instanceof ObservableList.ElementClearedEvent

    def elementClearedEvent = event as ObservableList.ElementClearedEvent
    assert elementClearedEvent.values == [1, 2, 3]
    assert observable.size() == 0

To get an overview of all the supported event types the reader is encouraged to have a look at the JavaDoc documentation or the source code of the observable collection in use.

`ObservableMap` and `ObservableSet` come with the same concepts as we have seen for `ObservableList` in this section.