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

时间:2018-03-28 17:48:51

标签: java java-8 java-stream collectors

这是我在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>而不是原始Map<Long, Integer>返回。基本上,对于SampleTypeId; 1指HELLO,2指WORLD。

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

最后,我想知道是否也可以将其返回到Object而不是Map。所以,让我说我有一个对象; SummaryResult with (String) name and (int) result。因此,它返回List<SummaryResult>而不是原始Map<Long, Integer>。如何使用.map(或其他)功能执行此操作?或者还有其他方法吗?预期的产出将是这条线。

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

非常感谢@M先前给出的解释。普罗霍洛夫。

更新

更新到

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 个答案:

答案 0 :(得分:2)

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

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中的元素调用多次,所以如果convertType调用是&#34;重&#34 ; (例如,使用数据库或IO),然后最好将其作为toSummaryResultList转换的一部分进行调用,而不是作为流元素分类器。在这种情况下,您将从Map<Integer, Integer>类型的地图中收集,并在循环中使用convertType。我不会考虑添加任何代码,因为我认为这个变化是微不足道的。

答案 1 :(得分:0)

你确实可以使用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());

答案 2 :(得分:0)

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