使用 Java 8 个流聚合信息
Aggregate information using Java 8 streams
我仍在尝试完全掌握在 Java 8 中使用 Stream 包,希望得到一些帮助。
我有一个 class,如下所述,作为数据库调用的一部分,我在列表中收到了它的实例。
class VisitSummary {
String source;
DateTime timestamp;
Integer errorCount;
Integer trafficCount;
//Other fields
}
为了生成一些可能有用的信息,我有一个 class VisitSummaryBySource
,它包含所有访问的总和(对于给定的时间范围):
class VisitSummaryBySource {
String sourceName;
Integer recordCount;
Integer errorCount;
}
我希望构建一个 List<VisitSummaryBySource>
集合,顾名思义,它包含 VisitSummaryBySource
个对象的列表,其中包含每个不同来源的记录和遇到的错误的总数。
有没有一种方法可以在单个操作中使用流来实现这一点?或者我是否需要将其分解为多个操作?我能想到的最好的是:
Map<String, Integer> recordsBySrc = data.parallelStream().collect(Collectors.groupingBy(VisitSummaryBySource::getSource,
Collectors.summingInt(VisitSummaryBySource::getRecordCount)));
并计算误差
Map<String, Integer> errorsBySrc = data.parallelStream().collect(Collectors.groupingBy(VisitSummaryBySource::getSource,
Collectors.summingInt(VisitSummaryBySource::getErrorCount)));
并合并两张地图以得出我正在寻找的列表。
你走在正确的轨道上。 Collectors.summingInt
的使用是外部 groupingBy
收集器的 下游收集器 的示例。此操作从同一组中的每个 VisitSummaryBySource
个实例中提取一个整数值,并将它们相加。这本质上是对整数的缩减。
正如您所注意到的,问题在于您只能 extract/reduce 一个整数值,因此您必须执行第二次传递 extract/reduce 其他整数值。
关键是要考虑减少单个整数值而不是整个 VisitSummaryBySource
对象。 Reduction 采用 BinaryOperator
,它采用所讨论类型的两个实例并将它们合并为一个。这是通过向 VisitSummaryBySource
:
添加静态方法来实现的方法
static VisitSummaryBySource merge(VisitSummaryBySource a,
VisitSummaryBySource b) {
assert a.getSource().equals(b.getSource());
return new VisitSummaryBySource(a.getSource(),
a.getRecordCount() + b.getRecordCount(),
a.getErrorCount() + b.getErrorCount());
}
请注意,我们实际上并没有合并源名称。由于这种减少仅在源名称相同的组内执行,因此我们断言我们只能合并名称相同的两个实例。我们还假设明显的构造函数采用名称、记录计数和错误计数,并调用它来创建包含计数总和的合并对象。
现在我们的流看起来像这样:
Map<String, Optional<VisitSummaryBySource>> map =
data.stream()
.collect(groupingBy(VisitSummaryBySource::getSource,
reducing(VisitSummaryBySource::merge)));
请注意,此缩减会生成 Optional<VisitSummaryBySource>
类型的地图值。这有点奇怪;我们将在下面处理它。我们可以通过使用另一种形式的采用标识值的 reducing
收集器来避免 Optional
。这是可能的,但有点荒谬,因为身份的源名称没有好的价值。 (我们可以使用空字符串之类的东西,但我们必须放弃我们只合并源名称相同的对象的断言。)
我们并不真正关心地图;它只需要保持足够长的时间以减少 VisitSummaryBySource
个实例。完成后,我们可以使用 values()
提取地图值并丢弃地图。
我们还可以将其转回流并通过 Optional::get
映射来解包 Optional
。这是安全的,因为除非组中至少有一个成员,否则值永远不会在映射中结束。
最后,我们将结果收集到一个列表中。
最终代码如下所示:
List<VisitSummaryBySource> output =
data.stream()
.collect(groupingBy(VisitSummaryBySource::getSource,
reducing(VisitSummaryBySource::merge)))
.values().stream()
.map(Optional::get)
.collect(toList());
我仍在尝试完全掌握在 Java 8 中使用 Stream 包,希望得到一些帮助。
我有一个 class,如下所述,作为数据库调用的一部分,我在列表中收到了它的实例。
class VisitSummary {
String source;
DateTime timestamp;
Integer errorCount;
Integer trafficCount;
//Other fields
}
为了生成一些可能有用的信息,我有一个 class VisitSummaryBySource
,它包含所有访问的总和(对于给定的时间范围):
class VisitSummaryBySource {
String sourceName;
Integer recordCount;
Integer errorCount;
}
我希望构建一个 List<VisitSummaryBySource>
集合,顾名思义,它包含 VisitSummaryBySource
个对象的列表,其中包含每个不同来源的记录和遇到的错误的总数。
有没有一种方法可以在单个操作中使用流来实现这一点?或者我是否需要将其分解为多个操作?我能想到的最好的是:
Map<String, Integer> recordsBySrc = data.parallelStream().collect(Collectors.groupingBy(VisitSummaryBySource::getSource,
Collectors.summingInt(VisitSummaryBySource::getRecordCount)));
并计算误差
Map<String, Integer> errorsBySrc = data.parallelStream().collect(Collectors.groupingBy(VisitSummaryBySource::getSource,
Collectors.summingInt(VisitSummaryBySource::getErrorCount)));
并合并两张地图以得出我正在寻找的列表。
你走在正确的轨道上。 Collectors.summingInt
的使用是外部 groupingBy
收集器的 下游收集器 的示例。此操作从同一组中的每个 VisitSummaryBySource
个实例中提取一个整数值,并将它们相加。这本质上是对整数的缩减。
正如您所注意到的,问题在于您只能 extract/reduce 一个整数值,因此您必须执行第二次传递 extract/reduce 其他整数值。
关键是要考虑减少单个整数值而不是整个 VisitSummaryBySource
对象。 Reduction 采用 BinaryOperator
,它采用所讨论类型的两个实例并将它们合并为一个。这是通过向 VisitSummaryBySource
:
static VisitSummaryBySource merge(VisitSummaryBySource a,
VisitSummaryBySource b) {
assert a.getSource().equals(b.getSource());
return new VisitSummaryBySource(a.getSource(),
a.getRecordCount() + b.getRecordCount(),
a.getErrorCount() + b.getErrorCount());
}
请注意,我们实际上并没有合并源名称。由于这种减少仅在源名称相同的组内执行,因此我们断言我们只能合并名称相同的两个实例。我们还假设明显的构造函数采用名称、记录计数和错误计数,并调用它来创建包含计数总和的合并对象。
现在我们的流看起来像这样:
Map<String, Optional<VisitSummaryBySource>> map =
data.stream()
.collect(groupingBy(VisitSummaryBySource::getSource,
reducing(VisitSummaryBySource::merge)));
请注意,此缩减会生成 Optional<VisitSummaryBySource>
类型的地图值。这有点奇怪;我们将在下面处理它。我们可以通过使用另一种形式的采用标识值的 reducing
收集器来避免 Optional
。这是可能的,但有点荒谬,因为身份的源名称没有好的价值。 (我们可以使用空字符串之类的东西,但我们必须放弃我们只合并源名称相同的对象的断言。)
我们并不真正关心地图;它只需要保持足够长的时间以减少 VisitSummaryBySource
个实例。完成后,我们可以使用 values()
提取地图值并丢弃地图。
我们还可以将其转回流并通过 Optional::get
映射来解包 Optional
。这是安全的,因为除非组中至少有一个成员,否则值永远不会在映射中结束。
最后,我们将结果收集到一个列表中。
最终代码如下所示:
List<VisitSummaryBySource> output =
data.stream()
.collect(groupingBy(VisitSummaryBySource::getSource,
reducing(VisitSummaryBySource::merge)))
.values().stream()
.map(Optional::get)
.collect(toList());