map element in stream corresponding to post
I often find myself doing something like this:
list.stream().min(new Comparator<>() {
@Override
public int compare(E a, E b) {
return Double.compare(f(a),f(b));
}
})
where f
is a computation intensive function. This requires twice as many evaluations of f
as are actually necessary. I'd prefer to
list.stream().mapToDouble(f).min()
but then I don't know how to get the original element that this minimum corresponds to.
One ugly way around this is
class WithF<E>{
private final E e;
private final double fe;
WithF(E e, double fe){
this.e = e;
this.fe = fe;
}
public E getE(){
return e;
}
public double getFE(){
return fe;
}
}
and then
list.stream().map(e -> new WithF<>(e,f(e))).min(Comparator.comparingDouble(WithF::getFE))
Is there a better, idiomatic way of doing this thing with the stream API?
This transformation is often called the Schwartzian Transform
I'd actually generalize WithF
with a few extra bits, and rename it to make the pipe neater.
class SchwartzianKV<E, SORTKEY implements Comparable<SORTKEY> >
implements Comparable<SchwartzianKV<E, SORTKEY>> {
public final E e;
public final SORTKEY sortkey;
SchwartzianKV(E e, SORTKEY sortkey){
this.e = e;
this.sortkey = sortkey;
}
public static <E, SORTKEY implements Comparable<SORTKEY>>
Function<E, SchwartzianKV<E, SORTKEY>> transformer( Function<E, SORTKEY> fn ) {
return new Function<E, SchwartzianKV<E,SORTKEY>>() {
@Override SchwartzianKV<E,SORTKEY> apply(E e) {
return new SchwartzianKV<>(e, fn.apply(e));
}
}
}
public int compare(With<E> other) {
return sortkey.compare(other.sortkey);
}
}
Now you can write the stream as
Optional<E> minValue = list.stream()
.map( SchwartianKV.transformer( e -> f(e) ) )
.min()
.map( kv -> kv.e )
Which is pretty concise.
在等待时我会发布我目前正在考虑的内容:
List<Double> fs = list.stream()
.map(e -> f(e))
.collect(Collectors.toList())
int i = IntStream.range(0,fs.size()).boxed()
.min(java.util.Comparator.comparingDouble(fs::get))
.get();
list.get(i)
How about:
Optional<E> minE = list.stream()
.map(e -> new AbstractMap.SimpleEntry(e, f(e))
.min(Map.Entry.comparingByValue())
.map(Map.Entry::getKey);
That essentially creates a map entry for each of the list items to the double value then finds the min
entry using the values and finally gets the matching key.
Note that it returns an Optional
to allow for the situation in which there are no items in the list in which case you will get an empty
. Alternatively you could add a orElse
call to the end to return another E
if the list is empty.
It is a slightly unusual use of Map.Entry (ie not putting it in a map). There are libraries with a Pair
class which could do the same job or you could create your own.
上一篇: 冗余一致性的用户协议PFSubclassing(Swift 2.0)
下一篇: 流中的地图元素对应于帖子