Java 8 is featuring lambdas, which are similar to a construction Groovy has already for some time: closures.
In Groovy we could already do this:
def list = [ 'a' , 'b' , 'c' ] print list. collect { it.toUpperCase() } // [A, B, C] |
where {
it.toUpperCase() }
is the closure.
In Java 8 we can achieve the same functionality now in a concise way.
list.stream().map( s -> s.toUpperCase()
) |
Although you could argue that with proper use of the new Stream API, bulk operations and method references, at least the intent of a piece of code is conveyed more clearly now – Java’s verboseness can still cause sore eyes.
Here are some other examples.
1
2
3
4
5
6
7
8
9
10
11 |
class Animal { String name BigDecimal price String farmer String toString() { name } } 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" ) |
Example 1: Summing the total price of all animals
1
2 |
assert 9 == animals.sum { it.price } // or
animals.price.sum() |
What Groovy you see here:
sum
can be called on a List and optionally passed a closure defining the property of “it” – the animal being iterated over – to sort on.- or
sum
can be called on a List without any arguments, which is equivalent to invoking the “plus” method on all items in the collection.
1
2
3
4
5
6 |
Optional<BigDecimal> sum = animals. stream(). map(Animal::getPrice). reduce((l, r) -> l.add(r)); assert BigDecimal.valueOf( 9 ) == sum.get(); |
What Java you see here:
- Through the Stream API’s
stream
method we can create a pipeline of operations, such asmap
andreduce
- The argument to the
map
operation is a method reference to thegetPrice()
method of the currently iterated animal. We could also replace this part with the expressiona -> a.getPrice()
reduce
is a general reduction operation (also called a fold) in which theBigDecimals
of the prices are added up. This is also giving us anOptional
with the total sum.- BTW, if we were to use a double for price
– which we don’t because I want to give a
good example – we could have used an existing DoubleStream with
a
sum()
on it e.g.double
sum = animals.stream().mapToDouble(Animal::getPrice).sum();
Example 2: Grouping all animals by farmer
1
2 |
def animalsByFarmer = animals.groupBy { it.farmer } // [john:[Buttercup], dick:[Carmella,
Cinnamon]] |
1
2
3
4
5
6 |
Map<String, List<Animal>> animalsByFarmer = animals .stream() .collect( Collectors.groupingBy(Animal::getFarmer)); // {dick=[Carmella, Cinnamon],
john=[Buttercup]} |
Example 3: Summing the total price of all animals grouped by farmer
1
2
3
4
5 |
def totalPriceByFarmer = animals .groupBy { it.farmer } .collectEntries { k, v -> [k, v.price.sum()] } // [john:2,
dick:7] |
What Groovy you see here:
collectEntries
iterates through the “groupBy” map transforming each map entry using thek, v -> ...
closure returning a map of the transformed entries.v.price
is actually a List of prices (per farmer) – such as in example 1 – on which we can callsum()
.
1
2
3
4
5
6
7
8
9
10
11 |
Map<String, BigDecimal> totalPriceByFarmer = animals .stream() .collect( Collectors.groupingBy( Animal::getFarmer, Collectors.reducing( BigDecimal.ZERO, Animal::getPrice, BigDecimal::add))); // {dick=7,
john=2} |
This Java code again yields the same results. Since IDE’s, Eclipse at least, don’t format this properly, you’ll have to indent these kinds of constructions for readability a bit yourself.