如何确定流中对象不同属性的min和max?
我已经看到了有关如何获取相同变量的最小值和最大值的答案。我还看到了有关如何使用特定对象属性(例如 maxByAttribute()
)获取最小值或最大值的答案。但是如何获得流中对象的所有“x”属性的最小值和所有“y”属性的最大值?
假设我有一个 Java Stream<Span>
每个对象都有一个 Span.getStart()
和Span.getEnd()
返回类型long
。 (单位无关;可能是时间或地板上的木板。)我想获得最小开始和最大结束,例如表示覆盖所有跨度的最小跨度。当然,我可以创建一个循环并手动更新最小值和最大值,但是有没有一种使用 Java 流的简洁高效的函数方法?
请注意,我不想创建中间跨度!如果您想创建一些中间 Pair<Long>
可以使用的实例,但出于我的目的,Span
类型很特殊,我无法创建更多类型。我只想找到最小起点和最大终点。
还显示使用新的 Java 12 是否可以实现这一点 teeing()
,但就我的目的而言,该解决方案必须在 Java 8+ 中工作。
最佳答案
假设所有数据均有效 (end > start
),您可以创建 LongSummaryStatistics
通过使用 summaryStatistics()
作为终端操作,包含诸如最小/最大值、平均值等信息的对象。
List<Span> spans = // initiliazing the source
LongSummaryStatistics stat = spans.stream()
.flatMapToLong(span -> LongStream.of(span.getStart(), span.getEnd()))
.summaryStatistics();
long minStart = stat.getMin();
long maxEnd = stat.getMax();
注意,如果流源为空(您可以通过调用来检查它stat.getCount()
,将给出 LongSummaryStatistics
对象的消耗元素数量)、min 和 max 属性将有它们的默认值,分别是最大和最小长值。
这就是使用 collect()
并手动选择 max 和 min 值来完成的方法:
long[] minMax = spans.stream()
.collect(() -> new long[2],
(long[] arr, Span span) -> { // consuming the next value
arr[0] = Math.min(arr[0], span.getStart());
arr[1] = Math.max(arr[1], span.getEnd());
},
(long[] left, long[] right) -> { // merging partial results produced in different threads
left[0] = Math.min(left[0], right[0]);
left[1] = Math.max(left[1], right[1]);
});
为了使用Collectors.teeing()
,您需要定义两个收集器和一个函数。流中的每个元素将同时被两个收集器使用,当它们完成时,merger
函数将获取它们的中间结果并产生最终结果。
在下面的示例中,结果是 map 条目的可选
。如果流中没有元素,生成的可选对象也将为空。
List<Span> spans = List.of(new Span(1, 3), new Span(3, 6), new Span(7, 9));
Optional<Map.Entry<Long, Long>> minMaxSpan = spans.stream()
.collect(Collectors.teeing(
Collectors.minBy(Comparator.comparingLong(Span::getStart)),
Collectors.maxBy(Comparator.comparingLong(Span::getStart)),
(Optional<Span> min, Optional<Span> max) ->
min.isPresent() ? Optional.of(Map.entry(min.get().getStart(), max.get().getEnd())) : Optional.empty()));
minMaxSpan.ifPresent(System.out::println);
输出
1=9
作为替代数据载体,您可以使用 Java 16 记录:
public record MinMax(long start, long end) {}
编译器将生成 start()
和 end()
形式的 Getters。
关于Java 流独立变量的最小值/最大值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72144300/