Java 8 Lambdas vs Groovy Closures Compactness: Grouping And Summing
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.
Some Groovy animals
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
Groovy
assert 9 == animals.sum { it.price }
// or animals.price.sum()What Groovy you see here:
sumcan be called on a List and optionally passed a closure defining the property of “it” – the animal being iterated over – to sort on.- or
sumcan be called on a List without any arguments, which is equivalent to invoking the “plus” method on all items in the collection.
Java 8
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
streammethod we can create a pipeline of operations, such asmapandreduce - The argument to the
mapoperation is a method reference to thegetPrice()method of the currently iterated animal. We could also replace this part with the expressiona -> a.getPrice() reduceis a general reduction operation (also called a fold) in which theBigDecimalsof the prices are added up. This is also giving us anOptionalwith 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
Groovy
def animalsByFarmer = animals.groupBy { it.farmer }
// [john:[Buttercup], dick:[Carmella, Cinnamon]]Java 8
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
Groovy
def totalPriceByFarmer =
animals
.groupBy { it.farmer }
.collectEntries { k, v -> [k, v.price.sum()] }
// [john:2, dick:7]What Groovy you see here:
collectEntriesiterates through the “groupBy” map transforming each map entry using thek, v -> ...closure returning a map of the transformed entries.v.priceis actually a List of prices (per farmer) – such as in example 1 – on which we can callsum().
Java 8
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.
| Reference: | Java 8 Lambdas vs Groovy Closures Compactness: Grouping And Summing from our JCG partner Ted Vinke at the Ted Vinke’s Blog blog. |






