获得Java 8流的最小值和最大值的简明方法

有一种简洁的方法可以在一次传递中提取流的最小值和最大值(基于某个比较器)吗?

似乎有很多方法可以单独获取最小值和最大值,或者我可以将流排序为临时对象,例如:

List sorted = Stream.of(...).sorted().collect(Collectors.toList()); T min = sorted.get(0); T max = sorted.get(sorted.size() - 1); 

但这并不简洁,需要分配一个临时对象。 我宁愿不分配临时对象或在流中进行两次传递。 还有其他选择吗?

 Pair extent = Stream.of(...).??? 

如果这是经常需要的function,我们最好让Collector完成这项工作。 我们需要一个Stats类来保存count, min, max和factory方法来创建stats收集器。

 Stats stats = stringStream.collect(Stats.collector()) fooStream.collect(Stats.collector(fooComparator)) 

(也许更好的方便方法是Stats.collect(stream)

我做了一个Stats类的例子 –

https://gist.github.com/zhong-j-yu/ac5028573c986f7820b25ea2e74ed672

 public class Stats { int count; final Comparator comparator; T min; T max; public Stats(Comparator comparator) { this.comparator = comparator; } public int count(){ return count; } public T min(){ return min; } public T max(){ return max; } public void accept(T val) { if(count==0) min = max = val; else if(comparator.compare(val, min)<0) min = val; else if(comparator.compare(val, max)>0) max = val; count++; } public Stats combine(Stats that) { if(this.count==0) return that; if(that.count==0) return this; this.count += that.count; if(comparator.compare(that.min, this.min)<0) this.min = that.min; if(comparator.compare(that.max, this.max)>0) this.max = that.max; return this; } public static  Collector, Stats> collector(Comparator comparator) { return Collector.of( ()->new Stats<>(comparator), Stats::accept, Stats::combine, Collector.Characteristics.UNORDERED, Collector.Characteristics.IDENTITY_FINISH ); } public static > Collector, Stats> collector() { return collector(Comparator.naturalOrder()); } } 

如果您有一个整数流,则summarizingInt收集器可以正常工作。

 IntSummaryStatistics stats = Stream.of(2,4,3,2) .collect(Collectors.summarizingInt(Integer::intValue)); int min = stats.getMin(); int max = stats.getMax(); 

如果你有双打,你可以使用summarizingDoublecollections家。

 DoubleSummaryStatistics stats2 = Stream.of(2.4, 4.3, 3.3, 2.5) .collect(Collectors.summarizingDouble((Double::doubleValue))); 

将流的每个元素映射到一对,其中两个元素表示最小值和最大值; 然后通过获取分钟的最小值和最大值来减少对。

例如,使用一些Pair类和一些Comparator

 Comparator comparator = ...; Optional> minMax = list.stream() .map(i -> Pair.of(i /* "min" */, i /* "max" */)) .reduce((a, b) -> Pair.of( // The min of the min elements. comparator.compare(a.first, b.first) < 0 ? a.first : b.first, // The max of the max elements. comparator.compare(a.second, b.second) > 0 ? a.second : b.second)); 

使用任何可变Pair类的直接方法:

 final Pair pair = new Pair<>(); final Comparator comparator = ...; Stream.of(...).forEachOrdered(e -> { if(pair.first == null || comparator.compare(e, pair.first) < 0){ pair.first = e; } if(pair.second == null || comparator.compare(e, pair.second) > 0){ pair.second = e; } }); 

对于相当简洁的纯Java解决方案,您可以使用.peek()。 这不是真正的function,因为.peek()所做的任何事都是副作用。 但这确实在一次通过中完成所有操作,不需要排序,也不是太冗长。 有一个“临时”对象,AtomicRef,但你可能会分配一个局部var / ref来保持最小值和最大值。

 Comparator cmp = ... Stream source = ... final AtomicReference min = new AtomicReference(); Optional max = source.peek(t -> {if (cmp.compare(t,min.get()) < 0) min.set(t);}) .max(cmp); //Do whatever with min.get() and max.get() 

我想你需要那个

 IntStream myIntStream = IntStream.rangeClosed(1, 100); IntSummaryStatistics intStatistic = myIntStream.summaryStatistics(); System.out.println("Max: " + intStatistic.getMax() + " Min: " + intStatistic.getMin());