I have a function which computes a value from an object.
And I have code like this:
List.stream()
.max((t1, t2) -> Float.compare(complexFunction(x, t1), complexFunction(x,t2)))
.get();
This successfully gets me the element for which complexFunction
produces the highest value, but I want to get this value as well. What's the best way to do this?
I'm thinking that I need to create a map first with all the elements and the values, and then do the max operation after that. Is there anything better?
You could use a Map
, but using a class might be easier.
Define a record to hold each input float
, and its resulting computation. Note that a record can be defined locally within a method, or defined separately.
In the old days, some people used AbstractMap.SimpleEntry
, AbstractMap.SimpleImmutableEntry
, or their interface Map.Entry
, or in Java 9+ the convenient Map.entry(…)
method, as a tuple for holding multiple values together. In Java 16+, the need for that vanishes. The new Records feature makes the code more explicit about your invention of a type. Having named fields makes the meaning clear. All the inherited Object
methods being implicitly created by the compiler makes defining a record
utterly simple. And the fact that a record
can be defined locally keeps the code minimal and tidy.
Core example code:
record Comp( Float f , float computed ) { } // Locally defines an entire class to communicate data transparently and immutably.
Optional < Comp > compOptional =
list
.stream()
.map(
f -> new Comp( f , complexFunction( f ) )
)
.max(
Comparator.comparingDouble( Comp :: computed )
);
We could optimize a bit if we do not care about collecting all the Comp
records. We could avoid instantiation of all but the winning Comp
record by simply flipping the order of .map
and .max
.
record Comp( Float f , float computed ) { } // Locally defines an entire class to communicate data transparently and immutably.
Optional < Comp > compOptional =
list
.stream()
.max(
Comparator.comparingDouble( App :: complexFunction )
)
.map(
f -> new Comp( f , complexFunction( f ) )
);
Full example code:
package work.basil.example.modmax;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
public class App
{
public static void main ( String[] args )
{
App app = new App();
app.demo();
}
private void demo ( )
{
List < Float > list = List.of( 7F , 99F , 42F , 1F );
record Comp( Float f , float computed ) { } // Locally defines an entire class to communicate data transparently and immutably.
Optional < Comp > compOptional =
list
.stream()
.max(
Comparator.comparingDouble( App :: complexFunction )
)
.map(
f -> new Comp( f , complexFunction( f ) )
);
System.out.println( "compOptional = " + compOptional );
}
static float complexFunction ( Float f )
{
return f * 2;
}
}
When run:
Optional[Comp[f=99.0, computed=198.0]]
You could play around with Float
versus float
to minimize auto-boxing. But for small amounts of data infrequently processed, I would not bother.