JAVA8stream中三个参数的reduce⽅法对List进⾏分组统
计操作
背景
平时在编写前端代码时,习惯使⽤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
llect.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 = wArrayList(
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 = wArrayList();
fooList
.stream()
.
upingBy(Foo::List()))      .forEach((name,fooListByName)->{
Bar bar = new Bar();
bar = fooListByName
.stream()
.reduce(bar,(u,t)->u.sum(t),(u,t)->u);
System.out.String());
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;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
}
public Double getTypeValue() {
return typeValue;
}
public void setTypeValue(Double typeValue) {
}
public Integer getCount() {
return count;
}
public void setCount(Integer count) {
}
}
Bar.java
llect.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.bazList = wArrayList();
}
public Bar sum(Foo foo){
if(name == null){
this.name = Name();
}
this.bazList.add(new Type(),TypeValue()));
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) {
java stream}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("name:").append(this.name).append(System.lineSeparator());
sb.append("count:").unt).append(System.lineSeparator());
sb.append("totalTypeValue:").alTypeValue).append(System.lineSeparator());
sb.append("bazList:").append(System.lineSeparator());
this.bazList.forEach(baz->{
sb.append("\t").append("type:").Type()).append(System.lineSeparator());
sb.append("\t").append("typeValue:").TypeValue()).append(System.lineSeparator());    });
String();
}
}
Baz.java
public class Baz{
private String type;
private Double typeValue;
public Baz(String type, Double typeValue) {
}
public String getType() {
return type;
}
public void setType(String type) {
}
public Double getTypeValue() {
return typeValue;
}
public void setTypeValue(Double typeValue) {
}
}
PS
等下次有空补上不使⽤stream().reduce 实现同样操作的⽐较繁琐的代码,啦啦啦啦啦~~~
补充知识:Java8collect、reduce⽅法聚合操作详解
Stream的基本概念
Stream和集合的区别:
1.Stream不会⾃⼰存储元素。元素储存在底层集合或者根据需要产⽣。
2.Stream操作符不会改变源对象。相反,它会返回⼀个持有结果的新的Stream。
3.Stream操作可能是延迟执⾏的,这意味着它们会等到需要结果的时候才执⾏。
Stream操作的基本过程,可以归结为3个部分:
创建⼀个Stream。
在⼀个或者多个操作中,将指定的Stream转换为另⼀个Stream的中间操作。
通过终⽌(terminal)⽅法来产⽣⼀个结果。该操作会强制它之前的延时操作⽴即执⾏,这之后该Stream就不能再被使⽤了。中间操作都是filter()、distinct()、sorted()、map()、flatMap()等,其⼀般是对数据集的整理(过滤、排序、匹配、抽取等)。
终⽌⽅法往往是完成对数据集中数据的处理,如forEach(),还有allMatch()、anyMatch()、findAny()、
findFirst(),数值计算类的⽅法有sum、max、min、average等等。终⽌⽅法也可以是对集合的处理,如reduce()、 collect()等等。
reduce()⽅法的处理⽅式⼀般是每次都产⽣新的数据集,⽽collect()⽅法是在原数据集的基础上进⾏更新,过程中不产⽣新的数据集。
List nums = Arrays.asList(1, 3, null, 8, 7, 8, 13, 10);
nums.stream().filter(num -> num != null).distinct().forEach(System.out::println);
上⾯代码实现为过滤null值并去重,遍历结果,实现简洁明了。使⽤传统⽅法就相对繁琐的多。另外其中 forEach即为终⽌操作⽅法,如果⽆该⽅法上⾯代码就没有任何操作。
filter、map、forEach、findAny等⽅法的使⽤都⽐较简单,这⾥省略。
下⾯介绍强⼤的聚合操作,其主要分为两种:
可变聚合:把输⼊的元素们累积到⼀个可变的容器中,⽐如Collection或者StringBuilder;
其他聚合:除去可变聚合,剩下的,⼀般都不是通过反复修改某个可变对象,⽽是通过把前⼀次的汇聚结果当成下⼀次的⼊参,反复如此。⽐如reduce,count,allMatch;
聚合操作reduce
这⾥主要介绍reduce⽅法:
T reduce(T identity, BinaryOperator accumulator)
identity:它允许⽤户提供⼀个循环计算的初始值。accumulator:计算的累加器,其⽅法签名为apply(T t,U u),在该reduce⽅法中第⼀个参数t为上次函数计算的返回值,第⼆个参数u为Stream中的元素,这个函数把这两个值计算apply,得到的和会被赋值给下次执⾏这个⽅法的第⼀个参数。有点绕看代码:
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);
这个例⼦中100即为计算初始值,每次相加计算值都会传递到下⼀次计算的第⼀个参数。
reduce还有其它两个重载⽅法:
Optional reduce(BinaryOperatoraccumulator):与上⾯定义基本⼀样,⽆计算初始值,所以他返回的是⼀个Optional。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。