欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JAVA8 stream中三個(gè)參數(shù)的reduce方法對List進(jìn)行分組統(tǒng)計(jì)操作

 更新時(shí)間:2020年08月26日 10:17:13   作者:weixin_33912246  
這篇文章主要介紹了JAVA8 stream中三個(gè)參數(shù)的reduce方法對List進(jìn)行分組統(tǒng)計(jì)操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧

背景

平時(shí)在編寫前端代碼時(shí),習(xí)慣使用lodash來編寫‘野生'的JavaScript;

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

使用這些‘野生'的API能夠極大的提高我本人編寫JS代碼的效率。而JAVA8開始支持stream和lambda表達(dá)式,這些和lodash的API有很多類似的功能。因此我在熟悉lodash的前提下嘗試使用JAVA8的新特性減少冗余代碼的編寫。

需求

在開發(fā)后端某功能接口的過程中,需要對一個(gè)從數(shù)據(jù)庫中取出的數(shù)據(jù)List<T>進(jìn)行按照ID進(jìn)行聚合統(tǒng)計(jì)

JAVA8 reduce API

API個(gè)人理解

<U> U reduce(U u,BiFunction<U,? super T,U> accumulator,BinaryOperator<U> combiner) #第一個(gè)參數(shù)返回實(shí)例u,傳遞你要返回的U類型對象的初始化實(shí)例u #第二個(gè)參數(shù)累加器accumulator,可以使用二元ℷ表達(dá)式(即二元lambda表達(dá)式),聲明你在u上累加你的數(shù)據(jù)來源t的邏輯 #例如(u,t)->u.sum(t),此時(shí)lambda表達(dá)式的行參列表是返回實(shí)例u和遍歷的集合元素t,函數(shù)體是在u上累加t #第三個(gè)參數(shù)組合器combiner,同樣是二元ℷ表達(dá)式,(u,t)->u #lambda表達(dá)式行參列表同樣是(u,t),函數(shù)體返回的類型則要和第一個(gè)參數(shù)的類型保持一致

偽代碼

 #1.聲明一個(gè)返回結(jié)果U
 #2.對List<T>進(jìn)行遍歷,在U和每個(gè)T實(shí)例上應(yīng)用一個(gè)累加器進(jìn)行累加操作
 #3.返回最終結(jié)果U
 U result = identity;
 for (T element : this stream)
   result = accumulator.apply(result, element)
 return result;

數(shù)據(jù)準(zhǔn)備

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

講了那么多廢話,這個(gè)才是最直接的

代碼執(zhí)行大意

對 List<Foo> 按照name分組統(tǒng)計(jì)得到 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);
      });
  }
  /*
  輸出結(jié)果
  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

等下次有空補(bǔ)上不使用stream().reduce 實(shí)現(xiàn)同樣操作的比較繁瑣的代碼,啦啦啦啦啦~~~

補(bǔ)充知識:Java8collect、reduce方法聚合操作詳解

Stream的基本概念

Stream和集合的區(qū)別:

1.Stream不會自己存儲元素。元素儲存在底層集合或者根據(jù)需要產(chǎn)生。

2.Stream操作符不會改變源對象。相反,它會返回一個(gè)持有結(jié)果的新的Stream。

3.Stream操作可能是延遲執(zhí)行的,這意味著它們會等到需要結(jié)果的時(shí)候才執(zhí)行。

Stream操作的基本過程,可以歸結(jié)為3個(gè)部分:

創(chuàng)建一個(gè)Stream。

在一個(gè)或者多個(gè)操作中,將指定的Stream轉(zhuǎn)換為另一個(gè)Stream的中間操作。

通過終止(terminal)方法來產(chǎn)生一個(gè)結(jié)果。該操作會強(qiáng)制它之前的延時(shí)操作立即執(zhí)行,這之后該Stream就不能再被使用了。

中間操作都是filter()、distinct()、sorted()、map()、flatMap()等,其一般是對數(shù)據(jù)集的整理(過濾、排序、匹配、抽取等)。

終止方法往往是完成對數(shù)據(jù)集中數(shù)據(jù)的處理,如forEach(),還有allMatch()、anyMatch()、findAny()、 findFirst(),數(shù)值計(jì)算類的方法有sum、max、min、average等等。終止方法也可以是對集合的處理,如reduce()、 collect()等等。

reduce()方法的處理方式一般是每次都產(chǎn)生新的數(shù)據(jù)集,而collect()方法是在原數(shù)據(jù)集的基礎(chǔ)上進(jìn)行更新,過程中不產(chǎn)生新的數(shù)據(jù)集。

List nums = Arrays.asList(1, 3, null, 8, 7, 8, 13, 10);

nums.stream().filter(num -> num != null).distinct().forEach(System.out::println);

上面代碼實(shí)現(xiàn)為過濾null值并去重,遍歷結(jié)果,實(shí)現(xiàn)簡潔明了。使用傳統(tǒng)方法就相對繁瑣的多。另外其中 forEach即為終止操作方法,如果無該方法上面代碼就沒有任何操作。

filter、map、forEach、findAny等方法的使用都比較簡單,這里省略。

下面介紹強(qiáng)大的聚合操作,其主要分為兩種:

可變聚合:把輸入的元素們累積到一個(gè)可變的容器中,比如Collection或者StringBuilder;

其他聚合:除去可變聚合,剩下的,一般都不是通過反復(fù)修改某個(gè)可變對象,而是通過把前一次的匯聚結(jié)果當(dāng)成下一次的入?yún)?,反?fù)如此。比如reduce,count,allMatch;

聚合操作reduce

Stream.reduce,返回單個(gè)的結(jié)果值,并且reduce操作每處理一個(gè)元素總是創(chuàng)建一個(gè)新值。常用的方法有average, sum, min, max, count,使用reduce方法都可實(shí)現(xiàn)。

這里主要介紹reduce方法:

T reduce(T identity, BinaryOperator accumulator)

identity:它允許用戶提供一個(gè)循環(huán)計(jì)算的初始值。accumulator:計(jì)算的累加器,其方法簽名為apply(T t,U u),在該reduce方法中第一個(gè)參數(shù)t為上次函數(shù)計(jì)算的返回值,第二個(gè)參數(shù)u為Stream中的元素,這個(gè)函數(shù)把這兩個(gè)值計(jì)算apply,得到的和會被賦值給下次執(zhí)行這個(gè)方法的第一個(gè)參數(shù)。有點(diǎn)繞看代碼:

int value = Stream.of(1, 2, 3, 4).reduce(100, (sum, item) -> sum + item);
Assert.assertSame(value, 110);
/* 或者使用方法引用 */
value = Stream.of(1, 2, 3, 4).reduce(100, Integer::sum);

這個(gè)例子中100即為計(jì)算初始值,每次相加計(jì)算值都會傳遞到下一次計(jì)算的第一個(gè)參數(shù)。

reduce還有其它兩個(gè)重載方法:

Optional reduce(BinaryOperatoraccumulator):與上面定義基本一樣,無計(jì)算初始值,所以他返回的是一個(gè)Optional。

U reduce(U identity, BiFunction accumulator, BinaryOperator combiner):與前面兩個(gè)參數(shù)的reduce方法幾乎一致,你只要注意到BinaryOperator其實(shí)實(shí)現(xiàn)了BiFunction和BinaryOperator兩個(gè)接口。

收集結(jié)果collect

當(dāng)你處理完流時(shí),通常只是想查看一下結(jié)果,而不是將他們聚合為一個(gè)值。先看collect的基礎(chǔ)方法,它接受三個(gè)參數(shù):

R collect(Supplier supplier, BiConsumer accumulator, BiConsumer combiner)

supplier:一個(gè)能創(chuàng)造目標(biāo)類型實(shí)例的方法。accumulator:一個(gè)將當(dāng)元素添加到目標(biāo)中的方法。combiner:一個(gè)將中間狀態(tài)的多個(gè)結(jié)果整合到一起的方法(并發(fā)的時(shí)候會用到)。接著看代碼:

Stream stream = Stream.of(1, 2, 3, 4).filter(p -> p > 2);

List result = stream.collect(() -> new ArrayList<>(), (list, item) -> list.add(item), (one, two) -> one.addAll(two));
/* 或者使用方法引用 */
result = stream.collect(ArrayList::new, List::add, List::addAll);

這個(gè)例子即為過濾大于2的元素,將剩余結(jié)果收集到一個(gè)新的list中。

第一個(gè)方法生成一個(gè)新的ArrayList;

第二個(gè)方法中第一個(gè)參數(shù)是前面生成的ArrayList對象,第二個(gè)參數(shù)是stream中包含的元素,方法體就是把stream中的元素加入ArrayList對象中。第二個(gè)方法被反復(fù)調(diào)用直到原stream的元素被消費(fèi)完畢;

第三個(gè)方法也是接受兩個(gè)參數(shù),這兩個(gè)都是ArrayList類型的,方法體就是把第二個(gè)ArrayList全部加入到第一個(gè)中;

代碼有點(diǎn)繁瑣,或者使用collect的另一個(gè)重載方法:

R collect(Collector collector)

注意到Collector其實(shí)是上面supplier、accumulator、combiner的聚合體。那么上面代碼就變成:

List list = Stream.of(1, 2, 3, 4).filter(p -> p > 2).collect(Collectors.toList());

將結(jié)果收集到map中

先定義如下Person對象

class Person{
  public String name;
  public int age;

  Person(String name, int age){
   this.name = name;
   this.age = age;
  }

  @Override
  public String toString(){
   return String.format("Person{name='%s', age=%d}", name, age);
  }
 }

假設(shè)你有一個(gè)Stream對象,希望將其中元素收集到一個(gè)map中,這樣就可以根據(jù)他的名稱來查找對應(yīng)年齡,例如:

Map result = people.collect(HashMap::new,(map,p)->map.put(p.name,p.age),Map::putAll); /*使用Collectors.toMap形式*/

Map result = people.collect(Collectors.toMap(p -> p.name, p -> p.age, (exsit, newv) -> newv));

其中Collectors.toMap方法的第三個(gè)參數(shù)為鍵值重復(fù)處理策略,如果不傳入第三個(gè)參數(shù),當(dāng)有相同的鍵時(shí),會拋出一個(gè)IlleageStateException。

或者你想將Person分解為Map存儲:

List<Map> personToMap = people.collect(ArrayList::new, (list, p) -> {
  Mapmap = new HashMap<>();
  map.put("name", p.name);
  map.put("age", p.age);
  list.add(map);
}, List::addAll);

分組和分片

對具有相同特性的值進(jìn)行分組是一個(gè)很常見的任務(wù),Collectors提供了一個(gè)groupingBy方法,方法簽名為:

Collector<T,?,Map> groupingBy(Function classifier, Collector downstream)

classifier:一個(gè)獲取Stream元素中主鍵方法。downstream:一個(gè)操作對應(yīng)分組后的結(jié)果的方法。

假如要根據(jù)年齡來分組:

Map<Integer, List> peopleByAge = people.filter(p -> p.age > 12).collect(Collectors.groupingBy(p -> p.age, Collectors.toList()));

假如我想要根據(jù)年齡分組,年齡對應(yīng)的鍵值List存儲的為Person的姓名,怎么做呢:

Map<Integer, List> peopleByAge = people.collect(Collectors.groupingBy(p -> p.age, Collectors.mapping((Person p) -> p.name, Collectors.toList())));

mapping即為對各組進(jìn)行投影操作,和Stream的map方法基本一致。

假如要根據(jù)姓名分組,獲取每個(gè)姓名下人的年齡總和(好像需求有些坑爹):

Map sumAgeByName = people.collect(Collectors.groupingBy(p -> p.name, Collectors.reducing(0, (Person p) -> p.age, Integer::sum)));

/* 或者使用summingInt方法 */

sumAgeByName = people.collect(Collectors.groupingBy(p -> p.name, Collectors.summingInt((Person p) -> p.age)));

可以看到Java8的分組功能相當(dāng)強(qiáng)大,當(dāng)然你還可以完成更復(fù)雜的功能。另外Collectors中還存在一個(gè)類似groupingBy的方法:partitioningBy,它們的區(qū)別是partitioningBy為鍵值為Boolean類型的groupingBy,這種情況下它比groupingBy更有效率。

join和統(tǒng)計(jì)功能

話說Java8中新增了一個(gè)StringJoiner,Collectors的join功能和它基本一樣。用于將流中字符串拼接并收集起來,使用很簡單:

String names = people.map(p->p.name).collect(Collectors.joining(","))

Collectors分別提供了求平均值averaging、總數(shù)couting、最小值minBy、最大值maxBy、求和suming等操作。但是假如你希望將流中結(jié)果聚合為一個(gè)總和、平均值、最大值、最小值,那么Collectors.summarizing(Int/Long/Double)就是為你準(zhǔn)備的,它可以一次行獲取前面的所有結(jié)果,其返回值為(Int/Long/Double)SummaryStatistics。

DoubleSummaryStatistics dss = people.collect(Collectors.summarizingDouble((Person p)->p.age));
double average=dss.getAverage();
double max=dss.getMax();
double min=dss.getMin();
double sum=dss.getSum();
double count=dss.getCount();

以上這篇JAVA8 stream中三個(gè)參數(shù)的reduce方法對List進(jìn)行分組統(tǒng)計(jì)操作就是小編分享給大家的全部內(nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • JVM GC 垃圾收集梳理總結(jié)

    JVM GC 垃圾收集梳理總結(jié)

    這篇文章主要介紹了JVM GC 垃圾收集梳理總結(jié),GC是一種自動的存儲管理機(jī)制。當(dāng)一些被占用的內(nèi)存不再需要時(shí),就應(yīng)該予以釋放,這種存儲資源管理,稱為垃圾回收
    2022-07-07
  • WeakHashMap的使用方法詳解

    WeakHashMap的使用方法詳解

    這篇文章主要介紹了WeakHashMap的使用方法詳解的相關(guān)資料,希望通過本文能幫助到大家,讓大家理解掌握這部分內(nèi)容,需要的朋友可以參考下
    2017-10-10
  • Java中ThreadLocal的用法及原理詳解

    Java中ThreadLocal的用法及原理詳解

    這篇文章主要介紹了Java中ThreadLocal的用法及原理詳解,在并發(fā)編程中,如果一個(gè)類變量被多個(gè)線程操作,會造成線程安全問題,使用ThreadLocal可以讓每個(gè)線程擁有線程內(nèi)部的變量,防止多個(gè)線程操作一個(gè)類變量造成的線程安全問題,需要的朋友可以參考下
    2023-09-09
  • Java中構(gòu)造、生成XML簡明教程

    Java中構(gòu)造、生成XML簡明教程

    這篇文章主要介紹了Java中構(gòu)造、生成XML簡明教程,本文通過dom4j包來完成,需要的朋友可以參考下
    2014-08-08
  • java基于socket傳輸zip文件功能示例

    java基于socket傳輸zip文件功能示例

    這篇文章主要介紹了java基于socket傳輸zip文件功能,結(jié)合實(shí)例形式分析了java使用socket進(jìn)行文件傳輸?shù)木唧w操作步驟與服務(wù)器端、客戶端相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2017-07-07
  • 深入理解mybatis的ParamNameResolver

    深入理解mybatis的ParamNameResolver

    ParamNameResolver是 MyBatis 中的一個(gè)重要組件,它為 MyBatis 提供了一種方便的方式來獲取方法參數(shù)的名稱,本文主要介紹了深入理解mybatis的ParamNameResolver,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-08-08
  • Struts2 通過ognl表達(dá)式實(shí)現(xiàn)投影

    Struts2 通過ognl表達(dá)式實(shí)現(xiàn)投影

    這篇文章主要介紹了Struts2 通過ognl表達(dá)式實(shí)現(xiàn)投影,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-09-09
  • SpringBoot與docker的結(jié)合的示例

    SpringBoot與docker的結(jié)合的示例

    本篇文章主要介紹了SpringBoot與docker的結(jié)合的示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-03-03
  • 一文徹底搞定Java中常用集合的排序方法

    一文徹底搞定Java中常用集合的排序方法

    在某些特殊的場景下我們需要在Java程序中對List集合進(jìn)行排序操作,下面這篇文章主要給大家介紹了關(guān)于Java中常用集合的排序方法的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-11-11
  • 淺談SpringBoot如何正確攔截thymeleaf異常

    淺談SpringBoot如何正確攔截thymeleaf異常

    Thymeleaf是一個(gè)模板引擎工具,主要用于頁面渲染操作,本文主要介紹了淺談SpringBoot如何正確攔截thymeleaf異常,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-09-09

最新評論