IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    使用JAVA8 stream中三个参数的reduce方法对List进行分组统计

    黑凤梨又开放发表于 2017-01-21 01:46:31
    love 0

    背景

    平时在编写前端代码时,习惯使用lodash来编写‘野生’的JavaScript;

    lodash提供来一套完整的API对JS对象(Array,Object,Collection等)进行操作,这其中就包括_.groupBy 和 _.reduce,即分组和'聚合'(reduce不知道该怎么翻译合适)。

    使用这些‘野生’的API能够极大的提高我本人编写JS代码的效率。而JAVA8开始支持stream和lambda表达式,这些和lodash的API有很多类似的功能。因此我在熟悉lodash的前提下尝试使用JAVA8的新特性减少冗余代码的编写。

    需求

    在开发后端某功能接口的过程中,需要对一个从数据库中取出的数据List<T>进行按照ID进行聚合统计

    JAVA8 reduce API

    API个人理解

      <U> U reduce(U u,BiFunction<U,? super T,U> accumulator,BinaryOperator<U> combiner)
      #第一个参数返回实例u,传递你要返回的U类型对象的初始化实例u
    
      #第二个参数累加器accumulator,可以使用二元ℷ表达式(即二元lambda表达式),声明你在u上累加你的数据来源t的逻辑
      #例如(u,t)->u.sum(t),此时lambda表达式的行参列表是返回实例u和遍历的集合元素t,函数体是在u上累加t
    
      #第三个参数组合器combiner,同样是二元ℷ表达式,(u,t)->u
      #lambda表达式行参列表同样是(u,t),函数体返回的类型则要和第一个参数的类型保持一致
    

    伪代码

      #1.声明一个返回结果U
      #2.对List<T>进行遍历,在U和每个T实例上应用一个累加器进行累加操作
      #3.返回最终结果U
      U result = identity;
      for (T element : this stream)
          result = accumulator.apply(result, element)
      return result;

    数据准备

    var source =
    [
        {"name": "A","type": "san","typeValue": 1.0,"count": 2},
        {"name": "A","type": "nas","typeValue": 13.0,"count": 1},
        {"name": "B","type": "san","typeValue": 112.0,"count": 3},
        {"name": "C","type": "san","typeValue": 43.0,"count": 5},
        {"name": "B","type": "nas","typeValue": 77.0,"count": 7}
    ];
    var target =
    [
        {
            "name": "A",
            "count": 3,
            "totalTypeValue": 14.0,
            "bazList": [
                {
                    "type": "san",
                    "typeValue": 1.0
                },
                {
                    "type": "nas"
                    "typeValue": 13.0
                }
            ]
        }, 
        {
            "name": "B",
            "count": 10,
            "totalTypeValue": 189.0,
            "bazList": [
                {
                    "type": "san",
                    "typeValue": 112.0
                }, {
                    "type": "nas"
                    "typeValue": 77.0
                }
            ]
        }, 
        {
            "name": "C",
            "count": 5,
            "totalTypeValue": 43.0,
            "bazList": [
                {
                    "type": "san",
                    "typeValue": 43.0
                }
            ]
        }
    ];
    

    Code

    • 讲了那么多废话,这个才是最直接的

    代码执行大意

    对 List<Foo> 按照name分组统计得到 List<Bar>

    ReduceTest.java

    import com.google.common.collect.Lists;
    import Bar;
    import Foo;
    
    import java.util.List;
    import java.util.stream.Collectors;
    
    
    public class ReduceTest {
        
        public static void main(String[] args) throws Exception{
            List<Foo> fooList = Lists.newArrayList(
                new Foo("A","san",1.0,2),
                new Foo("A","nas",13.0,1),
                new Foo("B","san",112.0,3),
                new Foo("C","san",43.0,5),
                new Foo("B","nas",77.0,7)
            );
            List<Bar> barList = Lists.newArrayList();
            fooList
                .stream()
                .collect(Collectors.groupingBy(Foo::getName,Collectors.toList()))
                .forEach((name,fooListByName)->{
                    Bar bar = new Bar();
                    bar = fooListByName
                            .stream()
                            .reduce(bar,(u,t)->u.sum(t),(u,t)->u);
                    System.out.println(bar.toString());
                    barList.add(bar);
                });
        }
        /*
        输出结果
        name:A
        count:3
        totalTypeValue:14.0
        bazList:
            type:san
            typeValue:1.0
            type:nas
            typeValue:13.0
        
        name:B
        count:10
        totalTypeValue:189.0
        bazList:
            type:san
            typeValue:112.0
            type:nas
            typeValue:77.0
        
        name:C
        count:5
        totalTypeValue:43.0
        bazList:
            type:san
            typeValue:43.0
        */
    }
    

    Foo.java

    public class Foo{
        private String name;
        private String type;
        private Double typeValue;
        private Integer count;
    
        public Foo(String name, String type, Double typeValue, Integer count) {
            this.name = name;
            this.type = type;
            this.typeValue = typeValue;
            this.count = count;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getType() {
            return type;
        }
    
        public void setType(String type) {
            this.type = type;
        }
    
        public Double getTypeValue() {
            return typeValue;
        }
    
        public void setTypeValue(Double typeValue) {
            this.typeValue = typeValue;
        }
    
        public Integer getCount() {
            return count;
        }
    
        public void setCount(Integer count) {
            this.count = count;
        }
    }
    

    Bar.java

    import com.google.common.collect.Lists;
    
    import java.util.List;
    
    public class Bar{
        private String name;
        private Integer count;
        private Double totalTypeValue;
        private List<Baz> bazList;
    
        public Bar() {
            this.name = null;
            this.count = 0;
            this.totalTypeValue = 0.0;
            this.bazList = Lists.newArrayList();
        }
    
        public Bar sum(Foo foo){
            if(name == null){
                this.name = foo.getName();
            }
            this.count += foo.getCount();
            this.totalTypeValue += foo.getTypeValue();
            this.bazList.add(new Baz(foo.getType(),foo.getTypeValue()));
            return this;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getCount() {
            return count;
        }
    
        public void setCount(Integer count) {
            this.count = count;
        }
    
        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append("name:").append(this.name).append(System.lineSeparator());
            sb.append("count:").append(this.count).append(System.lineSeparator());
            sb.append("totalTypeValue:").append(this.totalTypeValue).append(System.lineSeparator());
            sb.append("bazList:").append(System.lineSeparator());
            this.bazList.forEach(baz->{
                sb.append("\t").append("type:").append(baz.getType()).append(System.lineSeparator());
                sb.append("\t").append("typeValue:").append(baz.getTypeValue()).append(System.lineSeparator());
            });
            return sb.toString();
        }
    }
    

    Baz.java

    public class Baz{
        private String type;
        private Double typeValue;
    
        public Baz(String type, Double typeValue) {
            this.type = type;
            this.typeValue = typeValue;
        }
    
        public String getType() {
            return type;
        }
    
        public void setType(String type) {
            this.type = type;
        }
    
        public Double getTypeValue() {
            return typeValue;
        }
    
        public void setTypeValue(Double typeValue) {
            this.typeValue = typeValue;
        }
    }
    

    PS

    等下次有空补上不使用stream().reduce 实现同样操作的比较繁琐的代码,啦啦啦啦啦~~~



沪ICP备19023445号-2号
友情链接