java 获取Java 8流的最小值和最大值的简洁方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/41816264/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 06:10:57  来源:igfitidea点击:

Concise way to get both min and max value of Java 8 stream

javajava-stream

提问by Mzzzzzz

Is there a concise way to extract both the min and max value of a stream (based on some comparator) in one pass?

是否有一种简洁的方法可以一次性提取流的最小值和最大值(基于某个比较器)?

There appear to be many ways to get the min and max values individually, or I can sort the stream into a temporary object, for example:

似乎有很多方法可以单独获取最小值和最大值,或者我可以将流分类到一个临时对象中,例如:

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

But this isn't concise and requires allocating a temporary object. I'd rather not allocate a temporary object or make two passes through the stream. Is there an alternative?

但这并不简洁,需要分配一个临时对象。我宁愿不分配临时对象或通过流进行两次传递。有替代方案吗?

Pair<T> extent = Stream.of(...).???

采纳答案by ZhongYu

If this is a frequently needed feature, we better make a Collectorto do the job. We'll need a Statsclass to hold count, min, max, and factory methods to creat stats collector.

如果这是一个经常需要的功能,我们最好做一个Collector来完成这项工作。我们需要一个Stats类来保存count, min, max,以及工厂方法来创建统计收集器。

Stats<String> stats = stringStream.collect(Stats.collector())

fooStream.collect(Stats.collector(fooComparator))

(Maybe a better convenience method would be Stats.collect(stream))

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

I made an example Statsclass -

我做了一个示例Stats类 -

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

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

public class Stats<T>
{
    int count;

    final Comparator<? super T> comparator;
    T min;
    T max;

    public Stats(Comparator<? super T> 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<T> combine(Stats<T> 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 <T> Collector<T, Stats<T>, Stats<T>> collector(Comparator<? super T> comparator)
    {
        return Collector.of(
            ()->new Stats<>(comparator),
            Stats::accept,
            Stats::combine,
            Collector.Characteristics.UNORDERED, Collector.Characteristics.IDENTITY_FINISH
        );
    }

    public static <T extends Comparable<? super T>> Collector<T, Stats<T>, Stats<T>> collector()
    {
        return collector(Comparator.naturalOrder());
    }
}

回答by Will Humphreys

The summarizingIntcollector works well if you have a Stream of Integers.

summarizingInt收集工作得很好,如果你有一个整数流。

IntSummaryStatistics stats = Stream.of(2,4,3,2)
      .collect(Collectors.summarizingInt(Integer::intValue));

int min = stats.getMin();
int max = stats.getMax();

If you have doubles you can use the summarizingDoublecollector.

如果你有双打,你可以使用summarizingDouble收集器。

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

回答by Andy Turner

Map each element of the stream to a pair, where the two elements represent the min and the max; and then reduce the pairs by taking the min of the mins, and the max of the maxes.

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

For example, using some Pairclass and some Comparator<T>:

例如,使用一些Pair类和一些Comparator<T>

Comparator<T> comparator = ...;
Optional<Pair<T, T>> 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));

回答by Terlan Abdullayev

I think you need that

我想你需要那个

IntStream myIntStream = IntStream.rangeClosed(1, 100);
IntSummaryStatistics intStatistic = myIntStream.summaryStatistics();

System.out.println("Max: " + intStatistic.getMax() + " Min: " + intStatistic.getMin());

回答by WillD

For a pure Java solution that's fairly concise, you can use .peek(). This is not truly Functional, as anything that .peek() does is a side-effect. But this does do it all in one pass, doesn't require sorting and isn't too verbose. There is a "temp" Object, the AtomicRef, but you'll probably allocate a local var/ref to hold the min and max anyway.

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

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

回答by Calculator

A straightforward approach using any mutable Pairclass:

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

final Pair<T, T> pair = new Pair<>();
final Comparator<T> 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;
    }
});