Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Stream: find an element with a min/max value of an attribute

I have a stream of objects and I would like to find the one with a maximal value of some attribute that's expensive to calculate.

As a specific simple example, say that we have a list of strings and we want to find the coolest one, given a coolnessIndex function.

The following should work:

String coolestString = stringList         .stream()         .max((s1, s2) -> Integer.compare(coolnessIndex(s1), coolnessIndex(s2)))         .orElse(null); 

Now, there are two problems with this. First, assuming the coolnessIndex is expensive to calculate, this probably won't be very efficient. I suppose the max method will need to use the comparator repeatedly, which in turn will call the coolnessIndex repeatedly and at the end it will be called more than once for each string.

Second, having to provide the comparator leads to some redundancy in the code. I would much prefer syntax like this:

String coolestString = stringList         .stream()         .maxByAttribute(s -> coolnessIndex(s))         .orElse(null); 

However, I haven't been able to find a matching method in the Stream API. This surprises me, since finding min/max by an attribute seems like a common pattern. I wonder if there's a better way than using the comparator (other than a for loop).

like image 816
Jan Pomikálek Avatar asked Apr 21 '16 16:04

Jan Pomikálek


People also ask

How do you find the max and min in Java?

We can find Maximum and Minimum using simply iterate the HashSet and maintain the min and max variable and update it accordingly while traversing through each element and comparing it with the min and max values.

How do you find the maximum and minimum value of an array in Java 8?

With the introduction of Stream with Java 8, we can convert the array into the corresponding type stream using the Arrays. stream() method. Then we can call the max() and min() method, which returns the maximum and minimum element of this stream as OptionalInt .


2 Answers

Stream<String> stringStream = stringList.stream(); String coolest = stringStream.reduce((a,b)->      coolnessIndex(a) > coolnessIndex(b) ? a:b; ).get() 
like image 58
frhack Avatar answered Sep 22 '22 05:09

frhack


Here's a variant using an Object[] as a tuple, not the prettiest code but concise

String coolestString = stringList         .stream()         .map(s -> new Object[] {s, coolnessIndex(s)})         .max(Comparator.comparingInt(a -> (int)a[1]))         .map(a -> (String)a[0])         .orElse(null); 
like image 24
gustf Avatar answered Sep 25 '22 05:09

gustf