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

2023-09-07 01:30:50 作者:未来可期

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

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(...).???

推荐答案

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

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

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

fooStream.collect(Stats.collector(fooComparator))

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

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

我做了一个例子 Stats 类 -

I made an example Stats class -

https://gist.github.com/zhong-j-yu/ac5028573c986f7820b25ea2e74ed672p>

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());
    }
}