I'm trying to recreate a process to create a list of objects that are an aggregation of another list of objects using Java 8 Streams.
for example, I have a class, described below, that is provided from a database call or similar
public class Order {
private String orderNumber;
private String customerNumber;
private String customerGroup;
private Date deliveryDate;
private double orderValue;
private double orderQty;
}
Elsewhere in my application I have a class OrderTotal which represents and aggregation of Order grouping by customer number and group and summing the totals of orderValue and orderQty. (With an equals and hashcode on customerGroup and customerNumber)
public class OrderTotal {
private String customerGroup;
private String customerNumber;
private double totalValue;
private double totalQty;
}
The 'long hand' way we have achieved this prior to java 8 is as follows
public Collection<OrderTotal> getTotals(List<Order> orders) {
///map created for quick access to the order total for each order
Map<OrderTotal, OrderTotal> map = new HashMap<>();
///loop through all orders adding to the relevaent order total per iteration
for (Order order : orders) {
OrderTotal orderTotal = createFromOrder(order);
{
///if the order total already exists in the map use that one, otherwise add it to the map.
OrderTotal temp = map.get(orderTotal);
if(temp == null){
map.put(orderTotal, orderTotal);
}else{
orderTotal = temp;
}
}
///add the values to the total
aggregate(orderTotal, order);
}
return map.values();
}
private OrderTotal createFromOrder(Order order) {
OrderTotal orderTotal = new OrderTotal();
orderTotal.setCustomerGroup(order.getCustomerGroup());
orderTotal.setCustomerNumber(order.getCustomerNumber());
return orderTotal;
}
private void aggregate(OrderTotal orderTotal, Order order){
orderTotal.setTotalQty(orderTotal.getTotalQty() + order.getOrderQty());
orderTotal.setTotalValue(orderTotal.getTotalValue() + order.getOrderValue());
}
Ive been looking at Collectors using a grouping by and reduction functions but they all seem focused on aggregating the order class rather than composing the totals in the OrderTotal class.
I'm looking for a tidy stream or collect function that removes all the bloat from this code.
Aggregate operations − Stream supports aggregate operations like filter, map, limit, reduce, find, match, and so on. Pipelining − Most of the stream operations return stream itself so that their result can be pipelined.
Introduced in Java 8, the Stream API is used to process collections of objects. A stream is a sequence of objects that supports various methods which can be pipelined to produce the desired result. A stream is not a data structure instead it takes input from the Collections, Arrays or I/O channels.
Java 8 offers the possibility to create streams out of three primitive types: int, long and double. As Stream<T> is a generic interface, and there is no way to use primitives as a type parameter with generics, three new special interfaces were created: IntStream, LongStream, DoubleStream.
They process elements from a stream: Aggregate operations process elements from a stream, not directly from a collection. Consequently, they are also called stream operations. They support behavior as parameters: You can specify lambda expressions as parameters for most aggregate operations.
You can use the toMap
collector as follows:
Collection<OrderTotal> result = orders.stream()
.map(o -> createFromOrder(o))
.collect(toMap(Function.identity(),
Function.identity(),
(l, r) -> {
aggregate(l, r);
return l;
}))
.values();
Note that this requires changing the aggregate
method parameters to aggregate(OrderTotal orderTotal, OrderTotal order){ ... }
i.e. both parameters are of type OrderTotal
.
or you could remove the aggregate
method entirely and perform the logic in the toMap
:
Collection<OrderTotal> result = orders.stream()
.map(o -> createFromOrder(o))
.collect(toMap(Function.identity(),
Function.identity(),
(l, r) -> {
l.setTotalQty(l.getTotalQty() + r.getTotalQty());
l.setTotalValue(l.getTotalValue() + r.getTotalValue());
return l;
}))
.values();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With