组、收集器、映射(Int 到字符串)、映射(映射到对象)
Group, Collectors, Map (Int to String), Map (Map to Object)
这是我之前在 的问题的延续。
按照建议,我应该 post 作为一个单独的线程而不是更新原始线程。
所以对于我之前的一组问题,我已经做到了,现在,随着继续。
背景:
我有以下数据集
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}
有了这个,我想知道是否可以将其扩展为更多内容。
首先,我怎么能把它 return 作为 Map<String, Integer>
而不是原来的 Map<Long, Integer>
。基本上,对于 SampleTypeId; 1代表你好,2代表世界。
所以我需要一个 .map
(或者可能是其他函数)通过调用函数 say convertType(sampleTypeId)
? 将数据从 1 转换为 HELLO,将 2 转换为 WORLD。所以预期的输出将是 {"HELLO"=15, "WORLD"=10}
。那正确吗?我应该如何编辑当前建议的解决方案?
最后,我想知道是否也可以 return 将它变成对象而不是 Map
。所以假设我有一个对象; SummaryResult with (String) name and (int) result
。所以它 return 是 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";
}
你确实可以使用 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());
对于第一部分,考虑到你有方法
String convertType(int typeId)
你只需要从这个改变第一个分类器
groupingBy(SampleType::getTypeId)
至此
groupingBy(sample -> convertType(sample.getTypeId()))
其他一切保持不变。
后一种类型有点棘手,从技术上讲,它根本无法从 stream-related 解决方案中获益。
你需要的是这个:
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
调用是 "heavy" (例如,使用数据库或IO),那么最好将其称为toSummaryResultList
转换的一部分,而不是作为流元素分类器。在这种情况下,您仍将从类型为 Map<Integer, Integer>
的地图中收集数据,并在循环内使用 convertType
。考虑到这一点,我不会添加任何代码,因为我认为此更改微不足道。
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)));
这是我之前在
按照建议,我应该 post 作为一个单独的线程而不是更新原始线程。
所以对于我之前的一组问题,我已经做到了,现在,随着继续。
背景:
我有以下数据集
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}
有了这个,我想知道是否可以将其扩展为更多内容。
首先,我怎么能把它 return 作为 Map<String, Integer>
而不是原来的 Map<Long, Integer>
。基本上,对于 SampleTypeId; 1代表你好,2代表世界。
所以我需要一个 .map
(或者可能是其他函数)通过调用函数 say convertType(sampleTypeId)
? 将数据从 1 转换为 HELLO,将 2 转换为 WORLD。所以预期的输出将是 {"HELLO"=15, "WORLD"=10}
。那正确吗?我应该如何编辑当前建议的解决方案?
最后,我想知道是否也可以 return 将它变成对象而不是 Map
。所以假设我有一个对象; SummaryResult with (String) name and (int) result
。所以它 return 是 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";
}
你确实可以使用 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());
对于第一部分,考虑到你有方法
String convertType(int typeId)
你只需要从这个改变第一个分类器
groupingBy(SampleType::getTypeId)
至此
groupingBy(sample -> convertType(sample.getTypeId()))
其他一切保持不变。
后一种类型有点棘手,从技术上讲,它根本无法从 stream-related 解决方案中获益。
你需要的是这个:
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
调用是 "heavy" (例如,使用数据库或IO),那么最好将其称为toSummaryResultList
转换的一部分,而不是作为流元素分类器。在这种情况下,您仍将从类型为 Map<Integer, Integer>
的地图中收集数据,并在循环内使用 convertType
。考虑到这一点,我不会添加任何代码,因为我认为此更改微不足道。
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)));