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

前端 未结 6 1687
甜味超标
甜味超标 2020-12-15 03:53

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

相关标签:
6条回答
  • 2020-12-15 04:27

    The summarizingInt collector works well if you have a Stream of Integers.

    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 summarizingDouble collector.

    DoubleSummaryStatistics stats2 = Stream.of(2.4, 4.3, 3.3, 2.5)
      .collect(Collectors.summarizingDouble((Double::doubleValue)));
    
    0 讨论(0)
  • 2020-12-15 04:28

    I think you need that

    IntStream myIntStream = IntStream.rangeClosed(1, 100);
    IntSummaryStatistics intStatistic = myIntStream.summaryStatistics();
    
    System.out.println("Max: " + intStatistic.getMax() + " Min: " + intStatistic.getMin());
    
    0 讨论(0)
  • 2020-12-15 04:31

    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.

    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()
    
    0 讨论(0)
  • 2020-12-15 04:37

    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 Pair class and some 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));
    
    0 讨论(0)
  • 2020-12-15 04:40

    A straightforward approach using any mutable Pair class:

    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;
        }
    });
    
    0 讨论(0)
  • 2020-12-15 04:41

    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))
    

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

    I made an example Stats class -

    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());
        }
    }
    
    0 讨论(0)
提交回复
热议问题