Efficient way to group by a given list based on a key and collect in same list java 8

无人久伴 提交于 2020-01-06 12:39:07

问题


I have the below class:

class A{
  String property1;
  String property2;
  Double property3;
  Double property4;
}

So the property1 and property2 is the key.

class Key{
      String property1;
      String property2; 
}

I already have a list of A like below:

List<A> list=new ArrayList<>();

I want to group by using the key and add to another list of A in order to avoid having multiple items with same key in the list:

Function<A, Key> keyFunction= r-> Key.valueOf(r.getProperty1(), r.getProperty2());

But then while doing group by I have to take a sum of property3 and average of property4.

I need an efficient way to do it.

Note: I have skipped the methods of the given classes.


回答1:


Collecting to a Map is unavoidable since you want to group things. A brute-force way to do that would be :

yourListOfA
      .stream()
      .collect(Collectors.groupingBy(
             x -> new Key(x.getProperty1(), x.getProperty2()),
             Collectors.collectingAndThen(Collectors.toList(),
                   list -> {
                        double first = list.stream().mapToDouble(A::getProperty3).sum();
                        // or any other default
                        double second = list.stream().mapToDouble(A::getProperty4).average().orElse(0D);
                        A a = list.get(0);
                        return new A(a.getProperty1(), a.getProperty2(), first, second);
            })))
     .values();

This could be slightly improved for example in the Collectors.collectingAndThen to only iterate the List once, for that a custom collector would be required. Not that complicated to write one...




回答2:


Try like this:

 Map<A,List<A>> map = aList
                     .stream()
                     .collect(Collectors
                             .groupingBy(item->new A(item.property1,item.property2)));

List<A> result= map.entrySet().stream()
            .map(list->new A(list.getValue().get(0).property1,list.getValue().get(0).property1)
                    .avgProperty4(list.getValue())
                    .sumProperty3(list.getValue()))
            .collect(Collectors.toList());

and create avgProperty4 and sumProperty3 methods like to this

public A sumProperty3(List<A> a){
  this.property3 = a.stream().mapToDouble(A::getProperty3).sum();
  return this;
}

public A avgProperty4(List<A> a){
   this.property4 =  a.stream().mapToDouble(A::getProperty4).average().getAsDouble();
   return this;
}

result = aList.stream().collect(Collectors
            .groupingBy(item -> new A(item.property1, item.property2),
                    Collectors.collectingAndThen(Collectors.toList(), list ->
                            new A(list.get(0).property1, list.get(0).property1)
                                    .avgProperty4(list).sumProperty3(list))
            )
    );


来源:https://stackoverflow.com/questions/52041472/efficient-way-to-group-by-a-given-list-based-on-a-key-and-collect-in-same-list-j

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!