Java groupingBy: sum multiple fields

User1108 :

This question is an extension to the post: Java 8 groupingby with returning multiple field.

For the same problem, how do you return a list of Customer? For example, it should return:

Customer("A",4500,6500)
Customer("B",3000,3500)
Customer("C",4000,4500)
Ousmane D. :

@Pankaj Singhal's post is the right idea if you want a Map<String, Customer> as the result set +1. However, I would extract the merging logic into its own function e.g. in the Customer class you would have a function as such:

public static Customer merge(Customer first, Customer second) {
        first.setTotal(first.getTotal() + second.getTotal());
        first.setBalance(first.getBalance() + second.getBalance());
        return first;
}

Then the stream query would become:

Map<String, Customer> retObj = 
           listCust.stream()
                   .collect(Collectors.toMap(Customer::getName, Function.identity(), Customer::merge)); 
  • listCust.stream() creates a stream object i.e. Stream<Customer>.
  • collect performs a mutable reduction operation on the elements of this stream using the provided Collector.
  • The result of toMap is the provided collector, the toMap method extracts the keys Customer::getName and values Function.identity() and if the mapped keys contain duplicates, the merge function Customer::merge is used to resolve collisions.

There are three benefits I see with extracting the merging logic into its own function:

  • The code is more compact.
  • The code is more readable.
  • The complexity of the merging is isolated away from the stream.

if however, your intention is to retrieve a Collection<Customer>:

Collection<Customer> result = listCust.stream()
                .collect(Collectors.toMap(Customer::getName,
                        Function.identity(),
                        Customer::merge))
                .values();

or List<Customer> as the result set then all you have to do is call values() and pass the result of that to the ArrayList constructor:

List<Customer> result = new ArrayList<>(listCust.stream()
                .collect(Collectors.toMap(Customer::getName,
                        Function.identity(),
                        Customer::merge))
                .values());

Update:

if you don't want to mutate the objects in the source then simply modify the merge function as follows:

public static Customer merge(Customer first, Customer second) {
        Customer customer = new Customer(first.getName(), first.getTotal(), first.getBalance());
        customer.setTotal(customer.getTotal() + second.getTotal());
        customer.setBalance(customer.getBalance() + second.getBalance());
        return customer;
}

and everthing else stays as is.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=72975&siteId=1