Group, Collectors, Map (Int to String), Map (Map to Object)

喜你入骨 提交于 2019-12-04 19:53:10

For first part, considering you have somewhere the method

String convertType(int typeId)

You simply need to change first classifier from this

groupingBy(SampleType::getTypeId)

to this

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

Everything else remains the same.

Latter type is a little trickier, and technically doesn't benefit from it being a stream-related solution at all.

What you need is this:

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

You can use this as part of collector composition, where your whole collector will get wrapped into a collectingAndThen call:

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

However, as you can see, it is the whole collector that gets wrapped, so there is no real benefit in my eyes to the above version to a simpler and easier to follow (at least to me) version below that uses an intermediate variable, but isn't so much of a wall of code:

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

Another point to consider in above is: convertType method will be called as many times as there are elements in sampleList, so if convertType call is "heavy" (for example, uses database or IO), then it's better to call it as part of toSummaryResultList conversion, not as stream element classifier. In which case you will be collecting from map of type Map<Integer, Integer> still, and using convertType inside the loop. I will not add any code with this in consideration, as I view this change as trivial.

You could indeed use a map() function

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());
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)));
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!