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

组、收集器、映射(Int到字符串)、映射(映射到对象)

  •  1
  • user1778855  · 技术社区  · 6 年前

    这是我上一个问题的继续 Group, Sum byType then get diff using Java streams

    正如建议的那样,我应该作为一个单独的线程发布,而不是更新原来的线程。

    因此,通过我之前的一系列问题,我已经做到了这一点,现在,通过继续。

    背景:

    我有以下数据集

    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)
    

    我目前正在使用:

    sampleList.stream()
        .collect(Collectors.groupingBy(Sample::getTypeId,
            Collectors.summingInt(
                sample -> SampleType.ADD.equalsIgnoreCase(sample.getSampleType())
                    ? sample.getSampleQuantity() :
                    -sample.getSampleQuantity()
                )));
    

    还有这个

    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))));
    

    作为可接受的答案,以在 Map<Long, Integer> :

    {1=15, 2=10}
    

    有了这些,我想知道,这是否可以扩展到其他方面。

    首先,我怎样才能让它作为 Map<String, Integer> 而不是原来的 地图<长整数(&T); 。基本上,对于SampleTypeId;1表示你好,2表示世界。

    所以我需要一个 .map (或者其他函数)通过调用函数say将数据从1转换为HELLO,从2转换为WORLD convertType(sampleTypeId) ?。因此,预期输出将是 {"HELLO"=15, "WORLD"=10} 。是这样吗?我应该如何编辑此问题的当前建议解决方案?

    最后,我想知道是否也可以将其返回到对象而不是 Map 。假设我有一个目标; SummaryResult with (String) name and (int) result 。所以它返回一个 List<SummaryResult> 而不是原来的 地图<长整数(&T); 。如何使用 。地图 (或其他)功能来执行此操作?还是有其他方法可以做到这一点?预期的输出将是沿着这条线的东西。

    SummaryResult(name="hello", result=15), 
    SummaryResult(name="world", result=10), 
    

    非常感谢@M.Prokhorov之前给出的步骤解释。

    更新时间:

    更新到后

    sampleList.stream()
            .collect(Collectors.groupingBy(sample -> convertType(sample.getSampleTypeId()),
                    Collectors.collectingAndThen(
                        Collectors.groupingBy(Sample::getSampleType,
                            Collectors.summingInt(Sample::getSampleQuantity)),
                                map -> map.getOrDefault(SampleType.ADD, 0)
                                        - map.getOrDefault(SampleType.SUBTRACT, 0))));
    
    private String convertType(int id) {
        return (id == 1) ? "HELLO" : "WORLD";
    }
    
    3 回复  |  直到 6 年前
        1
  •  2
  •   M. Prokhorov    6 年前

    对于第一部分,考虑到你有某种方法

    String convertType(int typeId)
    

    您只需更改第一个分类器

    groupingBy(SampleType::getTypeId)
    

    到这个

    groupingBy(sample -> convertType(sample.getTypeId()))
    

    其他一切都保持不变。

    后一种类型有点棘手,从技术上讲,它根本不能从与流相关的解决方案中获益。

    您需要的是:

    public List<SummaryResult> toSummaryResultList(Map<String, Integer> resultMap) {
      List<SummaryResult> list = new ArrayList<>(resultMap.size());
      for (Map.Entry<String, Integer> entry : resultMap.entrySet()) {
        String name = entry.getKey();
        Integer value = entry.getValue();
    
        // replace below with construction method you actually have
        list.add(SummaryResult.withName(name).andResult(value));
      }
      return list;
    }
    

    您可以将其用作收集器组合的一部分,其中整个收集器将被包装到 collectingAndThen 电话:

    collectingAndThen(
      groupingBy(sample -> convertType(sample.getTypeId()),
         collectingAndThen(
           groupingBy(Sample::getSampleType,
             summingInt(Sample::getSampleQuantity)),
             map -> map.getOrDefault(SampleType.ADD, 0)
                    - map.getOrDefault(SampleType.SUBTRACT, 0))),
      result -> toSummaryResultList(result))
    

    然而,正如您所看到的,是整个收集器得到了包装,因此在我看来,使用中间变量的更简单、更容易遵循(至少对我来说)的下面版本对上面的版本没有真正的好处,但它并不是一堵代码墙:

    // do the whole collecting thing like before
    Map<String, Integer> map = sampleList.stream()
        .collect(Collectors.groupingBy(sample -> convertType(sample.getTypeId()),
                Collectors.collectingAndThen(
                    Collectors.groupingBy(Sample::getSampleType,
                        Collectors.summingInt(Sample::getSampleQuantity)),
                            map -> map.getOrDefault(SampleType.ADD, 0)
                                    - map.getOrDefault(SampleType.SUBTRACT, 0))));
    
    // return the "beautified" result
    return toSummaryResultList(map);
    

    上面要考虑的另一点是: convertType 方法的调用次数与 sampleList ,所以如果 转换器类型 调用是“重的”(例如,使用数据库或IO),那么最好将其作为 toSummaryResultList 转换,而不是流元素分类器。在这种情况下,您将从类型的映射中收集 Map<Integer, Integer> 仍然,并使用 转换器类型 在回路内部。我不会添加任何考虑到这一点的代码,因为我认为这一更改微不足道。

        2
  •  0
  •   alex440 Gangaraju    6 年前

    您确实可以使用map()函数

    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))))
            .entrySet()            
            .stream()            
            .map(entry->new SummaryResult(entry.getKey()),entry.getValue())
            .collect(Collectors.toList());
    
        3
  •  0
  •   zeugor    6 年前
    ToIntFunction<Sample> signedQuantityMapper= sample -> sample.getQuantity() 
        * (sample.getType() == Type.ADD ? 1 : -1);
    
    Function<Sample, String> keyMapper = s -> Integer.toString(s.getTypeId());
    
    Map<String, Integer> result = sampleList.stream().collect(
        Collectors.groupingBy(
            keyMapper,
            Collectors.summingInt(signedQuantityMapper)));