数据结构
List<Entity> list =
[
{name:"冀B1", t1: 20, t2: 30, ……, t12: 23},
{name:"冀B2", t1: 25, t2: 25, ……, t12: 52},
{name:"冀B3", t1: 31, t2: 24, ……, t12: 27},
{name:"冀B4", t1: 25, t2: 31, ……, t12: 29},
……
]
分别求t1,t2……,t12的合计。不是根据name
分组,不是根据name
分组,不是根据name
分组,是求所有数据t1的和,t2的和....t12的和.
结果
{
"t1":123.0,
"t2":250.0,
"t3":430.0,
"t4":0.0,
"t5":0.0,
"t6":65.0,
"t7":953.0,
"t8":0.0,
"t9":0.0,
"t10":0.0,
"t11":0.0,
"t12":0.0
}
我现在是用12次
list.stream().map(Entity::getTx).map(BigDecimal::new).reduce(new BigDecimal("0"), BigDecimal::add).doubleValue()
求和,有没有简单点的方法,没用sum()
是因为实际数据有小数精度问题
首先我还是吐吐槽,话说题主你也不给一个期盼的结果,你问题的描述在我看来其实不太清楚你想要什么结果,我指的是具体的返回类型,看了你下面自己的答复,才知道你想要个map
,虽然这个可能在你看来无伤大雅,但是我觉得又不是回答什么方案问题,你肯定是希望有一种代码写法,并且可以执行的,那给一个输入+输出的格式不好么,因为只要确认了输入输出,其实起码对于我们帮你想办法的人来说,就可以限定了很多写法和不必要的思考方向了,节约的大家的时间?▽?
还是言归正传,说说我自己的想法,这类问题其实我记得我也回答了几个了叭。。。
一般我的思路即: 当没有合适我需求的jdk stream api
提供时,都采用自定义Collector
的方式解决
题主的输入为List<Entity>
,输出为Map<String, Double>
,而jdk
中提供的返回map
的一般也是Collectors.toMap
,Collectors.groupBy
,但是他们都不太符合题主的需求,因此还是自定义Collector
的方式比较简单直接,当然题主已经给了一个回答了,在只要完成需求的目标前提下,其实都是可以的,所以我下面这种Collector
只是算提供另一个思路吧(如果从使用角度来说,Collector
使用起来相对简单)
@RequiredArgsConstructor
public class CustomSumByFieldCollector implements Collector<Entity, Map<String, BigDecimal>, Map<String, BigDecimal>> {
private final List<String> fieldNames;
private static final BiFunction<BeanMap, String, BigDecimal> GET_VALUE_FUN = (beanMap, fieldName) -> {
Double value = (Double) beanMap.getOrDefault(fieldName, Double.valueOf(0d));
return new BigDecimal(value);
};
@Override
public Supplier<Map<String, BigDecimal>> supplier() {
return HashMap::new;
}
@Override
public BiConsumer<Map<String, BigDecimal>, Entity> accumulator() {
return (map, entity) -> {
BeanMap beanMap = BeanMap.create(entity);
fieldNames.forEach(fieldName -> map.merge(fieldName, GET_VALUE_FUN.apply(beanMap, fieldName), BigDecimal::add));
};
}
@Override
public BinaryOperator<Map<String, BigDecimal>> combiner() {
return (map1, map2) -> {
map1.putAll(map2);
return map1;
};
}
@Override
public Function<Map<String, BigDecimal>, Map<String, BigDecimal>> finisher() {
return Function.identity();
}
@Override
public Set<Characteristics> characteristics() {
return Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));
}
}
上面有用到BeanMap
是spring-core
里的一个工具类,就是把entity
转换为map
,当然你可以使用其他的转换工具,例如apache
的BeanUtils
而使用这个Collector
的时候,直接new
一个CustomSumByFieldCollector
即可
public static void main(String[] args) {
List<Entity> list = new ArrayList<>();
List<String> keyList = Arrays.asList("t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9", "t10", "t11", "t12", "amount");
Map<String, Double> map = list.stream().collect(new CustomSumByFieldCollector(keyList));
}
由于题主也没有给测试用例,我也懒得写了哈,见谅,哈哈哈哈(* ̄ω ̄),所以这个代码没有测过,不过思路这样的,一般自定义Collector
可以解决100%
的stream
收集问题,起码对我来说100%
,哈哈哈,谁用谁说好,如果你对于自定义Collector
不太清楚,可以看一下我很早之前的一个说明吧
Java8 collector接口的定制实现
拜了个拜ヾ( ̄▽ ̄)
###用反射写了
List<String> keyList = Arrays.asList("t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9", "t10", "t11", "t12", "amount");
Map<String, Double> map = keyList.stream().collect(Collectors.toMap(e -> e, key -> list.stream().map(i -> {
try {
Field field = i.getClass().getDeclaredField(key);
field.setAccessible(true);
return Optional.ofNullable((Double) field.get(i)).orElse(0d);
} catch (Exception e) {
e.printStackTrace();
return 0d;
}
}).map(BigDecimal::new).reduce(BigDecimal.ZERO, BigDecimal::add).doubleValue()));
吐槽,四线小城市的软件公司真完蛋,即使是地头蛇企业