组,收集器,映射(Int到String),映射(映射到对象)
这是我之前在Group上的问题的延续,Sum byType然后使用Java流获得差异 。
正如所建议的那样,我应该发布一个单独的线程而不是更新原始线程。
因此,在我之前的一系列问题中,我已经实现了这一点,现在,继续进行。
背景:
我有以下数据集
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
获取所需输出以进行分组的已接受答案:
{1=15, 2=10}
有了这个,我想知道,如果这可以扩展到更多的东西。
首先,我怎么能让它作为Map
而不是原始的Map
。 基本上,对于SampleTypeId; 1指HELLO,2指WORLD。
所以我需要一个.map
(或者其他函数)来通过调用一个函数say convertType(sampleTypeId)
来将数据从1转换为HELLO,将2转换为WORLD。 因此预期的输出将是{"HELLO"=15, "WORLD"=10}
。 是对的吗? 我该如何编辑当前建议的解决方案呢?
最后,我想知道是否也可以将其返回到Object而不是Map
。 所以我想说我有一个对象; SummaryResult with (String) name and (int) result
。 因此它返回List
而不是原始Map
。 如何使用.map
(或其他)function执行此操作? 或者还有其他方法吗? 预期的产出将是这条线上的东西。
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"; }
对于第一部分,考虑到你有某个方法
String convertType(int typeId)
您只需要更改第一个分类器
groupingBy(SampleType::getTypeId)
对此
groupingBy(sample -> convertType(sample.getTypeId()))
其他一切都保持不变。
后期类型有点棘手,技术上并没有从它作为流相关的解决方案中受益。
你需要的是这个:
public List toSummaryResultList(Map resultMap) { List list = new ArrayList<>(resultMap.size()); for (Map.Entry 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 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
调用是“重”(例如,使用数据库或IO),那么最好将其作为一部分调用toSummaryResultList
转换,不作为流元素分类器。 在这种情况下,您将从Map
类型的Map
收集,并在循环内使用convertType
。 我不会考虑添加任何代码,因为我认为这个变化是微不足道的。
你确实可以使用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());
ToIntFunction signedQuantityMapper= sample -> sample.getQuantity() * (sample.getType() == Type.ADD ? 1 : -1); Function keyMapper = s -> Integer.toString(s.getTypeId()); Map result = sampleList.stream().collect( Collectors.groupingBy( keyMapper, Collectors.summingInt(signedQuantityMapper)));