Group,Sum byType然后使用Java流获得diff

use*_*855 3 java java-8 java-stream collectors

我想使用流来分组,按类型获取总和,然后按类型查找不同的结果.

所以这是我的数据集.

Sample(SampleId=1, SampleTypeId=1, SampleQuantity=5, SampleType=ADD), 
Sample(SampleId=2, SampleTypeId=1, SampleQuantity=15, SampleType=ADD), 
Sample(SampleId=3, SampleTypeId=1, SampleQuantity=25, SampleType=ADD), 
Sample(SampleId=4, SampleTypeId=1, SampleQuantity=5, SampleType=SUBTRACT), 
Sample(SampleId=5, SampleTypeId=1, SampleQuantity=25, SampleType=SUBTRACT) 
Sample(SampleId=6, SampleTypeId=2, SampleQuantity=10, SampleType=ADD), 
Sample(SampleId=7, SampleTypeId=2, SampleQuantity=20, SampleType=ADD), 
Sample(SampleId=8, SampleTypeId=2, SampleQuantity=30, SampleType=ADD), 
Sample(SampleId=9, SampleTypeId=2, SampleQuantity=15, SampleType=SUBTRACT), 
Sample(SampleId=10, SampleTypeId=2, SampleQuantity=35, SampleType=SUBTRACT)
Run Code Online (Sandbox Code Playgroud)

我目前的做法是这样的:

Map<Long, Integer> result = sampleList.stream()
.collect(
    Collectors.groupingBy(
        Sample::getSampleTypeId, 
        Collectors.summingInt(Sample::getSampleQuantity)
        )
    );
Run Code Online (Sandbox Code Playgroud)

我的结果是这样的(SampleTypeId,结果):

{1=75, 2=110}
Run Code Online (Sandbox Code Playgroud)

我不知道怎么从这里继续.基本上,有一个ADD或SUBTRACT的SampleType.所以我需要SUM所有TypeId = 1和Type = ADD AND SUM所有typeId = 1和Type = SUBSTRACT然后找到差异.

1 >> (5+15+25) - (5+25) = 15
2 >> (10+20+30) - (15+35) = 10
Run Code Online (Sandbox Code Playgroud)

所以预期的产量应该是{1=15, 2=10}.

M. *_*rov 5

收集器是Java 8中非常强大的功能,因为它们中的每一个都可以由不同的块组成,即使这样,也有一种简单的方法可以自己组合收集器.

这个解决方案通过这样的Collector组合解决,下面我将指导您完成解决方案的步骤.

首先,我们从第一个要求开始:结果必须是一个键入的字典TypeId:

Map<TypeId, List<Sample>> r = stream.collect(
  Collectors.groupingBy(Sample::getTypeId)
);
Run Code Online (Sandbox Code Playgroud)

其次,在每个TypeId列表中我们需要制作子组Type.为此,我们使用了Grouping By收集器的扩展版本,它允许我们指定在对元素进行分类并将它们分配给组后应该执行的操作.在以后的情况下,我们需要再次对它们进行分类:

Map<TypeId, Map<Type, List<Sample>>> r = stream.collect(
  groupingBy(Sample::getTypeId,
    groupingBy(Sample::getType))
);
Run Code Online (Sandbox Code Playgroud)

现在,一旦两个分类器完成,我们需要对组内所有元素之间的数量求和.技术是相同的:我们告诉分类器运行后如何处理组元素.在这种情况下,将所有金额加起来:

Map<TypeId, Map<Type, Integer>> r = stream.collect(
  groupingBy(Sample::getTypeId,
    groupingBy(Sample::getType, Collectors.summingInt(Sample::getQuantity))
  )
);
Run Code Online (Sandbox Code Playgroud)

接下来,最有趣的部分:我们需要对其进行操作Map<Type, Integer>.更具体地说,我们需要这样做map[Add] - map[Sub].这一步最重要的概念是,在返回结果之前,我们希望在完成所有其他收集操作后再执行此操作.这是一个只能在被称为流集合的阶段发生的操作Collector.finisher.我们可以通过调用类的collectingAndThen方法来组成任意收集器的终结器Collectors:

Map<TypeId, Integer> r = stream.collect(
  groupingBy(Sample::getTypeId,
    Collectors.collectingAndThen(
      groupingBy(Sample::getType, summingInt(Sample::getQuantity)),
      map -> map.getOrDefault(SampleType.ADD, 0) - map.getOrDefault(SampleType.SUBTRACT, 0)
    )
  )
);
Run Code Online (Sandbox Code Playgroud)

就这样.请注意,调用getOrDefault而不是调用很重要get,因为可能没有任何具有某些特定元素的元素,SampleType否则map会返回null,导致整个事物崩溃NullPointerException.