在stream中查找对应post-map minimum的pre-map元素
Find pre-map element in stream corresponding to post-map minimum
我经常发现自己在做这样的事情:
list.stream().min(new Comparator<>() {
@Override
public int compare(E a, E b) {
return Double.compare(f(a),f(b));
}
})
其中 f
是计算密集型函数。这需要 f
的评估次数是实际需要的两倍。我更愿意
list.stream().mapToDouble(f).min()
但是后来不知道怎么得到这个最小值对应的原始元素
一个丑陋的解决方法是
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;
}
}
然后
list.stream().map(e -> new WithF<>(e,f(e))).min(Comparator.comparingDouble(WithF::getFE))
有没有更好的、惯用的方法来处理流 API?
怎么样:
Optional<E> minE = list.stream()
.map(e -> new AbstractMap.SimpleEntry(e, f(e))
.min(Map.Entry.comparingByValue())
.map(Map.Entry::getKey);
这实际上为每个列表项创建一个映射条目到双精度值,然后使用这些值找到 min
条目并最终获得匹配的键。
请注意,它 return 是一个 Optional
以允许列表中没有项目的情况,在这种情况下您将得到一个 empty
。或者,如果列表为空,您可以在末尾添加一个 orElse
调用到 return 另一个 E
。
Map.Entry 的用法有点不寻常(即不将其放在地图中)。有 Pair
class 的图书馆可以完成同样的工作,或者您可以创建自己的图书馆。
等待期间我会post我目前正在考虑的事情:
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)
此转换通常称为 Schwartzian Transform
我实际上会用一些额外的位来概括 WithF
,并重命名它以使管道更整洁。
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);
}
}
现在您可以将流写为
Optional<E> minValue = list.stream()
.map( SchwartianKV.transformer( e -> f(e) ) )
.min()
.map( kv -> kv.e )
非常简洁。
list.stream()
.map(e -> new AbstractMap.SimpleImmutableEntry<>(e,f(e)))
.min(Map.Entry.comparingByValue())
.map(Map.Entry::getKey)
基本上使用 AbstractMap.SimpleImmutableEntry<E,Double>
代替问题中的 WithF<E>
。不是 Map.Entry
的预期用途,所以不理想。
我经常发现自己在做这样的事情:
list.stream().min(new Comparator<>() {
@Override
public int compare(E a, E b) {
return Double.compare(f(a),f(b));
}
})
其中 f
是计算密集型函数。这需要 f
的评估次数是实际需要的两倍。我更愿意
list.stream().mapToDouble(f).min()
但是后来不知道怎么得到这个最小值对应的原始元素
一个丑陋的解决方法是
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;
}
}
然后
list.stream().map(e -> new WithF<>(e,f(e))).min(Comparator.comparingDouble(WithF::getFE))
有没有更好的、惯用的方法来处理流 API?
怎么样:
Optional<E> minE = list.stream()
.map(e -> new AbstractMap.SimpleEntry(e, f(e))
.min(Map.Entry.comparingByValue())
.map(Map.Entry::getKey);
这实际上为每个列表项创建一个映射条目到双精度值,然后使用这些值找到 min
条目并最终获得匹配的键。
请注意,它 return 是一个 Optional
以允许列表中没有项目的情况,在这种情况下您将得到一个 empty
。或者,如果列表为空,您可以在末尾添加一个 orElse
调用到 return 另一个 E
。
Map.Entry 的用法有点不寻常(即不将其放在地图中)。有 Pair
class 的图书馆可以完成同样的工作,或者您可以创建自己的图书馆。
等待期间我会post我目前正在考虑的事情:
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)
此转换通常称为 Schwartzian Transform
我实际上会用一些额外的位来概括 WithF
,并重命名它以使管道更整洁。
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);
}
}
现在您可以将流写为
Optional<E> minValue = list.stream()
.map( SchwartianKV.transformer( e -> f(e) ) )
.min()
.map( kv -> kv.e )
非常简洁。
list.stream()
.map(e -> new AbstractMap.SimpleImmutableEntry<>(e,f(e)))
.min(Map.Entry.comparingByValue())
.map(Map.Entry::getKey)
基本上使用 AbstractMap.SimpleImmutableEntry<E,Double>
代替问题中的 WithF<E>
。不是 Map.Entry
的预期用途,所以不理想。