代码之家  ›  专栏  ›  技术社区  ›  user1778855

分组,按类型求和,然后使用Java流获取差异

  •  3
  • user1778855  · 技术社区  · 8 年前

    我想使用stream进行分组,按类型获取总和,然后按类型查找不同的结果。

    这是我的数据集。

    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)
    

    我目前的做法如下:

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

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

    {1=75, 2=110}
    

    我不知道如何从这里继续下去。基本上,有一个ADD或SUBTRACT的SampleType。所以我需要对所有的TypeId=1和Type=ADD求和,对所有的TypeId=1和Type=SUBSTRACT求和,然后找到差。

    实例

    1 >> (5+15+25) - (5+25) = 15
    2 >> (10+20+30) - (15+35) = 10
    

    因此,预期输出应为 {1=15, 2=10}

    4 回复  |  直到 8 年前
        1
  •  5
  •   M. Prokhorov    8 年前

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

    此解决方案通过以下方式解决 Collector 下面我将指导您完成解决方案的步骤。

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

    Map<TypeId, List<Sample>> r = stream.collect(
      Collectors.groupingBy(Sample::getTypeId)
    );
    

    第二,相互之间 类型ID 我们需要按以下方式创建子组的列表 Type 。为此,我们使用了按收集器分组的扩展版本,它允许我们在对元素进行分类并将其分配给组后指定应该执行的操作。在这种情况下,我们需要再次对其进行分类:

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

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

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

    接下来,最有趣的部分:我们需要在 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)
        )
      )
    );
    

    仅此而已。请注意,打电话很重要 getOrDefault 而不是 get ,因为可能没有任何元素 SampleType ,否则map将返回 null ,导致整个事情崩溃 NullPointerException

        2
  •  2
  •   Thiyagu    8 年前
    sampleList.stream()
            .collect(Collectors.groupingBy(Sample::getSampleTypeId,
                    Collectors.collectingAndThen(Collectors.groupingBy(Sample::getSampleType,
                            Collectors.summingInt(Sample::getSampleQuantity)),
                            map -> (map.getOrDefault(SampleType.ADD, 0) 
                                    - map.getOrDefault(SampleType.SUBTRACT, 0)))));
    

    外部 groupingBy 分组依据 sampleTypeId 。 在下一个层面上,它涉及到用键构建地图 SampleType 并计算 SampleQuantity 一把钥匙。这个 分页装订器 collectingAndThen 减去加法和减法的值得出最终结果。

    版本2(更简单):

    sampleList.stream()
            .collect(Collectors.groupingBy(Sample::getSampleTypeId,
                    Collectors.summingInt(sample -> sample.getSampleType() == SampleType.SUBTRACT
                            ? -sample.getSampleQuantity() :
                            sample.getSampleQuantity()
                    )));
    

    它不构建先前构建的第二个贴图。它只是检查SampleType是加法还是减法,并适当地返回sampleQuantity。

        3
  •  2
  •   fps    8 年前

    以下内容:

    1 >> ( 5 + 15 + 25) - ( 5 + 25) = 15
    2 >> (10 + 20 + 30) - (15 + 35) = 10
    

    与此完全相同:

    1 >>  5 + 15 + 25 -  5 - 25 = 15
    2 >> 10 + 20 + 30 - 15 - 35 = 10
    

    因此,您可以利用这一点,通过以下方式避免嵌套分组 SampleType 。相反,只需对下游收集器求和或求差即可:

    Map<Long, Integer> result = sampleList.stream()
        .collect(Collectors.groupingBy(
            Sample::getSampleTypeId, 
            Collectors.summingInt(sample -> sample.getSampleQuantity() * 
                    (SampleType.ADD.equals(sample.getSampleType()) ? 1 : -1))));
    

    如果您可以在 Sample 类别:

    public int getCalculatedQuantity() {
        return sampleQuantity * (SampleType.ADD.equals(sampleType) ? 1 : -1);
    }
    

    然后按如下方式使用:

    Map<Long, Integer> result = sampleList.stream()
        .collect(Collectors.groupingBy(
            Sample::getSampleTypeId, 
            Collectors.summingInt(Sample::getCalculatedQuantity)));
    
        4
  •  0
  •   alex440 Gangaraju    8 年前

    可能有一种更简单的方法,只需映射即可:

    Map<Long, Integer> result = sampleList.stream()
    .map(sample->{if(sample.getSampleType==SUBTRACT)
         return new Sample((sample.SampleId, 
                            sample.SampleTypeId, 
                            sample.SampleQuantity*(-1), 
                            sample.SampleType))
    
         return sample;
     })
    .collect(
        Collectors.groupingBy(
            Sample::getSampleTypeId, 
            Collectors.summingInt(Sample::getSampleQuantity)
            )
        );
    
    推荐文章
    twenty7  ·  按阵列列表分组
    11 年前