Although I suspect the answer to be "It's not specified"...
If there are multiple "greatest/lowest" elements in a Stream
which the Comparator
passed to the max or min methods considers equal (returns 0
), is it specified somewhere which element will be found?
It’s indeed hard to pull a definite statement from the documentation only. If we try to draw a conclusion from the general description of the “Reduction” process and similar hints of the documentation, it will always feel like we’re possibly doing too much interpretation.
However, there’s an explicit statement regarding this matter from Brian Goetz who’s quite an authority regarding the Stream API:
If the stream is ordered (such as the streams you get from an array or List), it returns the first element that is maximal in the event of multiple maximal elements; only if the stream is unordered is it allowed to pick an arbitrary element.
It’s a pity that such an explicit statement isn’t made right at the documentation of Stream.max
, but at least it’s in line with our experience and knowledge of the implementation (those of us who looked at the source code). And not to forget, practical considerations, as it’s easy to say “pick any rather than first” via unordered().max(comparator)
with the current state of affairs than saying “pick first rather than any” if max
was allowed to pick an arbitrary element in the first place.
After reading the source code, I think should be the first greatest element will be found according to the collection order.
We can check out the source code of the Stream.max(Comparator<? super T> comparator)
, the implementation class is ReferencePipeline.max
@Override
public final Optional<P_OUT> max(Comparator<? super P_OUT> comparator) {
return reduce(BinaryOperator.maxBy(comparator));
}
that you can see, when you call the Stream.max
, you mean call the Stream.reduce(BinaryOperator<P_OUT> accumulator)
And look at the source code of BinaryOperator.maxBy(comparator)
public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {
Objects.requireNonNull(comparator);
return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
}
It's clear, when a
equals b
, it returns a
. So when there are multiple "greatest/lowest" elements in a Stream, the "greatest/lowest" element should be the first "greatest/lowest" element according to the collection order
There is a example at blew, just for your reference.
List<Student> list = Arrays.asList(new Student("s1", 1), new Student("s2", 5), new Student("s3", 3), new Student("s4", 5));
// it should be student of 's2'
list.stream().max(Comparator.comparing(Student::getScore));
// it should be student of 's4'
list.stream().reduce((a, b) -> Comparator.comparing(Student::getScore).compare(a, b) > 0 ? a : b);