Ted Vinke
First8
September 2015
COLLECTIONS
groovyCollections.each { “working with $it” }
Overview
• Java Collections Basics
• Iterable & Collection
• Groovy Makes Things Easy
• List, Set, Range, Map
• GDK’s Iterable & Collection
• Some examples
• collate, collect, collectEntries, every and any, find,
findResults, groupBy, inject, removeAll, sum
• References
JAVA COLLECTIONS
BASICS
Where it all starts
Java
Collections
Iterable interface
• iterator
Iterator interface
• hasNext
• next
• remove
Collection interface
• add
• addAll
• clear
• contains(Object)
• containsAll(Collection)
• isEmpty
• iterator
• remove
• removeAll
• retainAll
• size
• toArray
GROOVY MAKES THINGS
EASY
Groovy Makes Things Easy
• Creating a collection
• list = new ArrayList() vs list = []
• map = new HashMap() vs map = [:]
• Working with elements
• Addition e.g. list.add(3) vs list << 3
• Retrieval e.g. map.get('color') vs map.color
• Overloading
• list1.addAll(list2) vs list1 + list2
List
def list = ['a', 'b']
list << 'c'
assert list.size() == 3
assert list.get(0) == 'a'
assert list instanceof java.util.List
def emptyList = []
assert emptyList.size() == 0
List is an ordered
collection of elements
Set
def list = ['a', 'b']
list << 'c'
list << 'c'
assert list.size() == 4
def set = list as Set
assert set.size() == 3
assert set instanceof java.util.Set
Set is a collection with no
duplicates
Range
def range = 5..8
assert range.size() == 4
assert range[2] == 7
assert range instanceof java.util.List
range = 'a'..'d'
assert range.size() == 4
assert range[2] == 'c'
assert range.from == 'a'
assert range.to == 'd'
Range is an extension of List
Map
def map = [name: 'Ted', age: 34]
map.color = 'blue'
assert map.size() == 3
assert map['name'] == 'Ted'
assert map instanceof java.util.Map
def emptyMap = [:]
assert emptyMap.size() == 0
Map maps keys to
values – e.g. a dictionary.
No duplicate keys.
Tip: def
“The keyword def is
great for examples, but
not always so much for
real API design.”
addAll, addAll, addAll, asBoolean, asImmuta
ble, asSynchronized, asType, collect, collec
t, collect, collectNested, each, eachWithInd
ex, find, find, findAll, findAll, findResult,fi
ndResult, flatten, getAt, getIndices, grep, g
rep, inject, inject, intersect, isCase, leftSh
ift, minus, plus, plus, plus, removeAll, remo
veAll, removeElement, retainAll, retainAll,
split, toListString, toListString, toSet, uni
que, unique, unique, unique, unique, unique
Groovy’s Collection
any, asCollection, asList, collate, collate, collate, col
late, collectEntries, collectEntries, collectEntries, c
ollectEntries, collectMany, collectMany, collectNeste
d,collectNested, combinations, combinations, contains
, containsAll, count, count, countBy, disjoint, drop, dr
opRight, dropWhile, each, eachCombination, eachPermu
tation,eachWithIndex, every, findResults, first, flatten
, flatten, getAt, groupBy, groupBy, groupBy, head, inde
xed, indexed, init, intersect, join, last, max, max, max,
min, min, min, minus,minus, multiply, permutations, pe
rmutations, plus, plus, size, sort, sort, sort, sort, sort
, sum, sum, sum, sum, tail, take, takeRight, takeWhile, t
oList, toSet, toSorted, toSorted,toSorted, toSpreadMapGroovy’s Iterable
SOME EXAMPLES
collate
def names = ["Lassy", "Buttercup", "Carmella", "Moo-moo", "Cinnamon"]
// collate into batches of 3
def collated = names.collate 3
assert collated == [['Lassy', 'Buttercup', 'Carmella'],
['Moo-moo', 'Cinnamon']]
// and passing a step of 1
collated = names.collate 3, 1
assert collated == [['Lassy', 'Buttercup', 'Carmella'],
['Buttercup', 'Carmella', 'Moo-moo'],
['Carmella', 'Moo-moo', 'Cinnamon'],
['Moo-moo', 'Cinnamon'],
['Cinnamon']]
Remember closures?
def adder = { a, b -> print a+b }
assert adder(1, 2) == 3
assert adder("m", "e") == "me"
Closures are used a lot!
• e.g. basic looping
• dogs.each { it.bark() }
• names.each { name ->
println “Hello $name”
}
• or whatever the
methods’ Javadoc
says
collect
def animals = []
animals << new Animal(name: "Buttercup", age: 2)
animals << new Animal(name: "Carmella", age: 5)
animals << new Animal(name: "Cinnamon", age: 2)
// collect on List
def names = animals.collect { it.name }
assert names == ["Buttercup", "Carmella", "Cinnamon"]
// names is java.util.ArrayList
// collect as a different type e.g. a Set, keeping only unique values
assert animals.collect(new HashSet()) { it.age }.toString() == "[2,
5]"
collectEntries
def john = new Farmer(name: "John")
def dick = new Farmer(name: "Dick")
def animals = []
animals << new Animal(name: "Buttercup", farmer: john)
animals << new Animal(name: "Carmella", farmer: dick)
animals << new Animal(name: "Cinnamon", farmer: dick)
// collect farmer name by animal name
def results = animals.collectEntries { animal ->
[animal.name, animal.farmer.name]
}
assert results == ['Buttercup':'John', 'Carmella':'Dick',
'Cinnamon':'Dick']
every and any
def animals = []
animals << new Animal(name: "Buttercup", age: 2)
animals << new Animal(name: "Carmella", age: 5)
animals << new Animal(name: "Cinnamon", age: 2)
// check if all animals are young
boolean allAreYoung = animals.every { it.age < 3 }
assert !allAreYoung // Carmella is, with age 5, not young
// check if atleast one of them is young
boolean atleastOneIsYoung = animals.any { it.age < 3 }
assert atleastOneIsYoung // Buttercup and Cinnamon are both
young enough
find
def animals = []
animals << new Animal(name: "Buttercup", age: 2)
animals << new Animal(name: "Carmella", age: 5)
animals << new Animal(name: "Cinnamon", age: 2)
// find first matching animal
Animal foundAnimal = animals.find { it.age == 2 }
assert foundAnimal.name == "Buttercup"
// find all matching animals
List foundAnimals = animals.findAll { it.name.startsWith "C" }
assert foundAnimals.toString() == "[Carmella, Cinnamon]"
findResults
def animals = []
animals << new Animal(name: "Buttercup", age: 2)
animals << new Animal(name: "Carmella", age: 5)
animals << new Animal(name: "Cinnamon", age: 2)
// find all non-null results
def greetings = animals.findResults {
it.age < 4 ? "Hello $it.name!" : null
}
assert greetings == ['Hello Buttercup!', 'Hello Cinnamon!']
groupBy
def animals = []
animals << new Animal(name: "Buttercup", country: "NL", age: 2)
animals << new Animal(name: "Carmella", country: "NL", age: 5)
animals << new Animal(name: "Cinnamon", country: "BE", age: 2)
// single closure
Map animalsByCountry = animals.groupBy { it.country }
assert animalsByCountry["NL"].size() == 2 // Buttercup and Carmella
assert animalsByCountry.BE.size() == 1 // Cinnamon
// list of closures
Map animalsByCountryAndAge = animals.groupBy( [{ it.country }, { it.age }]
)
assert animalsByCountryAndAge.toString() ==
"[NL:[2:[Buttercup], 5:[Carmella]], BE:[2:[Cinnamon]]]"
inject
def animals = []
animals << new Animal(name: "Buttercup", price: 2, farmer: "john")
animals << new Animal(name: "Carmella", price: 5, farmer: "dick")
animals << new Animal(name: "Cinnamon", price: 2, farmer: "dick")
// iteration, passing 1st item to closure, injecting results back into closure with 2nd
item, etc
assert 1 * 2 * 3 * 4 ==
[1, 2, 3, 4].inject { current, val -> current * val }
// gather the total price of all animals, passing initial price of 0
assert 9 == animals.inject(0) { totalPrice, animal ->
totalPrice + animal.price
}
// or...
assert 9 == animals.price.inject(0) { totalPrice, price ->
totalPrice + price
}
removeAll
def animals = []
animals << new Animal(name: "Buttercup", age: 2)
animals << new Animal(name: "Carmella", age: 5)
animals << new Animal(name: "Cinnamon", age: 2)
// remove all animals younger than 3
animals.removeAll { it.age < 3 }
assert animals.size() == 1
assert animals.first().name == "Carmella"
sum
def animals = []
animals << new Animal(name: "Buttercup", price: 2,
farmer: "john")
animals << new Animal(name: "Carmella", price: 5,
farmer: "dick")
animals << new Animal(name: "Cinnamon", price: 2,
farmer: "dick")
// gather the total price of all animals
assert 9 == animals.sum { it.price }
assert 9 == animals.price.sum()
Animals by farmer
// grouping
def animalsByFarmer =
animals.groupBy { it.farmer }
assert "[john:[Buttercup], dick:[Carmella,
Cinnamon]]" == animalsByFarmer.toString()
Total price per farmer
// grouping, collecting and summing
def totalPriceByFarmer = animals
.groupBy { it.farmer }
.collectEntries { k, v ->
[k, v.price.sum()]
}
assert ['john':2, 'dick':7] == totalPriceByFarmer
Tip: sort mutates
def animalList = createAnimals([])
assert animalList instanceof java.util.ArrayList
assert animalList.name == ["Lassy", "Buttercup", "Moo-moo"]
List sortedByAge = animalList.sort { it.age }
assert animalList.name == ["Moo-moo", "Lassy", "Buttercup"]
assert sortedByAge.name == ["Moo-moo", "Lassy", "Buttercup"]
assert animalList.is(sortedByAge) // is() is same as == in plain Java
def animalSet = createAnimals([] as Set)
assert animalSet instanceof java.util.HashSet
List sortedByName = animalSet.sort { it.name }
assert sortedByName.name == ["Buttercup", "Lassy", "Moo-moo"]
assert animalSet != sortedByName
assert !animalSet.is(sortedByName)
List againAnimalList = createAnimals([])
// pass false as the 1st parameter to indicate NOT to mutate original List
List againSortedByAge = againAnimalList.sort(false) { it.age }
assert againAnimalList.name == ["Lassy", "Buttercup", "Moo-moo"]
assert againSortedByAge.name == ["Moo-moo", "Lassy", "Buttercup"]
def createAnimals = { c ->
c << new Animal(name: "Lassy", age: 2)
c << new Animal(name: "Buttercup", age: 3)
c << new Animal(name: "Moo-moo", age: 1)
c
}
References
• Oracle Java™ Tutorials: Collections Trail
https://docs.oracle.com/javase/tutorial/collec
tions/index.html
• The Groovy Dev Kit: Working with Collections
http://groovy-lang.org/groovy-dev-
kit.html#_working_with_collections
• Groovy Closures
http://groovy-lang.org/closures.html

Working with Groovy Collections

  • 1.
  • 2.
    Overview • Java CollectionsBasics • Iterable & Collection • Groovy Makes Things Easy • List, Set, Range, Map • GDK’s Iterable & Collection • Some examples • collate, collect, collectEntries, every and any, find, findResults, groupBy, inject, removeAll, sum • References
  • 3.
  • 4.
  • 5.
    Iterable interface • iterator Iteratorinterface • hasNext • next • remove
  • 6.
    Collection interface • add •addAll • clear • contains(Object) • containsAll(Collection) • isEmpty • iterator • remove • removeAll • retainAll • size • toArray
  • 7.
  • 8.
    Groovy Makes ThingsEasy • Creating a collection • list = new ArrayList() vs list = [] • map = new HashMap() vs map = [:] • Working with elements • Addition e.g. list.add(3) vs list << 3 • Retrieval e.g. map.get('color') vs map.color • Overloading • list1.addAll(list2) vs list1 + list2
  • 9.
    List def list =['a', 'b'] list << 'c' assert list.size() == 3 assert list.get(0) == 'a' assert list instanceof java.util.List def emptyList = [] assert emptyList.size() == 0 List is an ordered collection of elements
  • 10.
    Set def list =['a', 'b'] list << 'c' list << 'c' assert list.size() == 4 def set = list as Set assert set.size() == 3 assert set instanceof java.util.Set Set is a collection with no duplicates
  • 11.
    Range def range =5..8 assert range.size() == 4 assert range[2] == 7 assert range instanceof java.util.List range = 'a'..'d' assert range.size() == 4 assert range[2] == 'c' assert range.from == 'a' assert range.to == 'd' Range is an extension of List
  • 12.
    Map def map =[name: 'Ted', age: 34] map.color = 'blue' assert map.size() == 3 assert map['name'] == 'Ted' assert map instanceof java.util.Map def emptyMap = [:] assert emptyMap.size() == 0 Map maps keys to values – e.g. a dictionary. No duplicate keys.
  • 13.
    Tip: def “The keyworddef is great for examples, but not always so much for real API design.”
  • 15.
    addAll, addAll, addAll,asBoolean, asImmuta ble, asSynchronized, asType, collect, collec t, collect, collectNested, each, eachWithInd ex, find, find, findAll, findAll, findResult,fi ndResult, flatten, getAt, getIndices, grep, g rep, inject, inject, intersect, isCase, leftSh ift, minus, plus, plus, plus, removeAll, remo veAll, removeElement, retainAll, retainAll, split, toListString, toListString, toSet, uni que, unique, unique, unique, unique, unique Groovy’s Collection
  • 16.
    any, asCollection, asList,collate, collate, collate, col late, collectEntries, collectEntries, collectEntries, c ollectEntries, collectMany, collectMany, collectNeste d,collectNested, combinations, combinations, contains , containsAll, count, count, countBy, disjoint, drop, dr opRight, dropWhile, each, eachCombination, eachPermu tation,eachWithIndex, every, findResults, first, flatten , flatten, getAt, groupBy, groupBy, groupBy, head, inde xed, indexed, init, intersect, join, last, max, max, max, min, min, min, minus,minus, multiply, permutations, pe rmutations, plus, plus, size, sort, sort, sort, sort, sort , sum, sum, sum, sum, tail, take, takeRight, takeWhile, t oList, toSet, toSorted, toSorted,toSorted, toSpreadMapGroovy’s Iterable
  • 17.
  • 18.
    collate def names =["Lassy", "Buttercup", "Carmella", "Moo-moo", "Cinnamon"] // collate into batches of 3 def collated = names.collate 3 assert collated == [['Lassy', 'Buttercup', 'Carmella'], ['Moo-moo', 'Cinnamon']] // and passing a step of 1 collated = names.collate 3, 1 assert collated == [['Lassy', 'Buttercup', 'Carmella'], ['Buttercup', 'Carmella', 'Moo-moo'], ['Carmella', 'Moo-moo', 'Cinnamon'], ['Moo-moo', 'Cinnamon'], ['Cinnamon']]
  • 19.
    Remember closures? def adder= { a, b -> print a+b } assert adder(1, 2) == 3 assert adder("m", "e") == "me"
  • 20.
    Closures are useda lot! • e.g. basic looping • dogs.each { it.bark() } • names.each { name -> println “Hello $name” } • or whatever the methods’ Javadoc says
  • 21.
    collect def animals =[] animals << new Animal(name: "Buttercup", age: 2) animals << new Animal(name: "Carmella", age: 5) animals << new Animal(name: "Cinnamon", age: 2) // collect on List def names = animals.collect { it.name } assert names == ["Buttercup", "Carmella", "Cinnamon"] // names is java.util.ArrayList // collect as a different type e.g. a Set, keeping only unique values assert animals.collect(new HashSet()) { it.age }.toString() == "[2, 5]"
  • 22.
    collectEntries def john =new Farmer(name: "John") def dick = new Farmer(name: "Dick") def animals = [] animals << new Animal(name: "Buttercup", farmer: john) animals << new Animal(name: "Carmella", farmer: dick) animals << new Animal(name: "Cinnamon", farmer: dick) // collect farmer name by animal name def results = animals.collectEntries { animal -> [animal.name, animal.farmer.name] } assert results == ['Buttercup':'John', 'Carmella':'Dick', 'Cinnamon':'Dick']
  • 23.
    every and any defanimals = [] animals << new Animal(name: "Buttercup", age: 2) animals << new Animal(name: "Carmella", age: 5) animals << new Animal(name: "Cinnamon", age: 2) // check if all animals are young boolean allAreYoung = animals.every { it.age < 3 } assert !allAreYoung // Carmella is, with age 5, not young // check if atleast one of them is young boolean atleastOneIsYoung = animals.any { it.age < 3 } assert atleastOneIsYoung // Buttercup and Cinnamon are both young enough
  • 24.
    find def animals =[] animals << new Animal(name: "Buttercup", age: 2) animals << new Animal(name: "Carmella", age: 5) animals << new Animal(name: "Cinnamon", age: 2) // find first matching animal Animal foundAnimal = animals.find { it.age == 2 } assert foundAnimal.name == "Buttercup" // find all matching animals List foundAnimals = animals.findAll { it.name.startsWith "C" } assert foundAnimals.toString() == "[Carmella, Cinnamon]"
  • 25.
    findResults def animals =[] animals << new Animal(name: "Buttercup", age: 2) animals << new Animal(name: "Carmella", age: 5) animals << new Animal(name: "Cinnamon", age: 2) // find all non-null results def greetings = animals.findResults { it.age < 4 ? "Hello $it.name!" : null } assert greetings == ['Hello Buttercup!', 'Hello Cinnamon!']
  • 26.
    groupBy def animals =[] animals << new Animal(name: "Buttercup", country: "NL", age: 2) animals << new Animal(name: "Carmella", country: "NL", age: 5) animals << new Animal(name: "Cinnamon", country: "BE", age: 2) // single closure Map animalsByCountry = animals.groupBy { it.country } assert animalsByCountry["NL"].size() == 2 // Buttercup and Carmella assert animalsByCountry.BE.size() == 1 // Cinnamon // list of closures Map animalsByCountryAndAge = animals.groupBy( [{ it.country }, { it.age }] ) assert animalsByCountryAndAge.toString() == "[NL:[2:[Buttercup], 5:[Carmella]], BE:[2:[Cinnamon]]]"
  • 27.
    inject def animals =[] animals << new Animal(name: "Buttercup", price: 2, farmer: "john") animals << new Animal(name: "Carmella", price: 5, farmer: "dick") animals << new Animal(name: "Cinnamon", price: 2, farmer: "dick") // iteration, passing 1st item to closure, injecting results back into closure with 2nd item, etc assert 1 * 2 * 3 * 4 == [1, 2, 3, 4].inject { current, val -> current * val } // gather the total price of all animals, passing initial price of 0 assert 9 == animals.inject(0) { totalPrice, animal -> totalPrice + animal.price } // or... assert 9 == animals.price.inject(0) { totalPrice, price -> totalPrice + price }
  • 28.
    removeAll def animals =[] animals << new Animal(name: "Buttercup", age: 2) animals << new Animal(name: "Carmella", age: 5) animals << new Animal(name: "Cinnamon", age: 2) // remove all animals younger than 3 animals.removeAll { it.age < 3 } assert animals.size() == 1 assert animals.first().name == "Carmella"
  • 29.
    sum def animals =[] animals << new Animal(name: "Buttercup", price: 2, farmer: "john") animals << new Animal(name: "Carmella", price: 5, farmer: "dick") animals << new Animal(name: "Cinnamon", price: 2, farmer: "dick") // gather the total price of all animals assert 9 == animals.sum { it.price } assert 9 == animals.price.sum()
  • 31.
    Animals by farmer //grouping def animalsByFarmer = animals.groupBy { it.farmer } assert "[john:[Buttercup], dick:[Carmella, Cinnamon]]" == animalsByFarmer.toString()
  • 32.
    Total price perfarmer // grouping, collecting and summing def totalPriceByFarmer = animals .groupBy { it.farmer } .collectEntries { k, v -> [k, v.price.sum()] } assert ['john':2, 'dick':7] == totalPriceByFarmer
  • 33.
    Tip: sort mutates defanimalList = createAnimals([]) assert animalList instanceof java.util.ArrayList assert animalList.name == ["Lassy", "Buttercup", "Moo-moo"] List sortedByAge = animalList.sort { it.age } assert animalList.name == ["Moo-moo", "Lassy", "Buttercup"] assert sortedByAge.name == ["Moo-moo", "Lassy", "Buttercup"] assert animalList.is(sortedByAge) // is() is same as == in plain Java def animalSet = createAnimals([] as Set) assert animalSet instanceof java.util.HashSet List sortedByName = animalSet.sort { it.name } assert sortedByName.name == ["Buttercup", "Lassy", "Moo-moo"] assert animalSet != sortedByName assert !animalSet.is(sortedByName) List againAnimalList = createAnimals([]) // pass false as the 1st parameter to indicate NOT to mutate original List List againSortedByAge = againAnimalList.sort(false) { it.age } assert againAnimalList.name == ["Lassy", "Buttercup", "Moo-moo"] assert againSortedByAge.name == ["Moo-moo", "Lassy", "Buttercup"] def createAnimals = { c -> c << new Animal(name: "Lassy", age: 2) c << new Animal(name: "Buttercup", age: 3) c << new Animal(name: "Moo-moo", age: 1) c }
  • 34.
    References • Oracle Java™Tutorials: Collections Trail https://docs.oracle.com/javase/tutorial/collec tions/index.html • The Groovy Dev Kit: Working with Collections http://groovy-lang.org/groovy-dev- kit.html#_working_with_collections • Groovy Closures http://groovy-lang.org/closures.html