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