使用JAVA8 stream中三個參數的reduce方法對List進行分組統計

背景

平時在編寫前端代碼時,習慣使用lodash來編寫‘野生’的JavaScript;前端

lodash提供來一套完整的API對JS對象(Array,Object,Collection等)進行操做,這其中就包括_.groupBy 和 _.reduce,即分組和'聚合'(reduce不知道該怎麼翻譯合適)。java

使用這些‘野生’的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>app

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 實現一樣操做的比較繁瑣的代碼,啦啦啦啦啦~~~ide

相關文章
相關標籤/搜索