在流中找到与地图后最小值对应的预映射元素

时间:2015-09-09 00:13:16

标签: java reduce

我经常发现自己在做这样的事情:

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是否有更好的,习惯性的方法?

4 个答案:

答案 0 :(得分:2)

此转换通常称为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 )

这非常简洁。

答案 1 :(得分:1)

怎么样:

get.se(...)

基本上为每个列表项创建一个映射条目到double值,然后使用这些值找到mean_cl_normal条目,最后得到匹配的键。

请注意,它会返回Optional<E> minE = list.stream() .map(e -> new AbstractMap.SimpleEntry(e, f(e)) .min(Map.Entry.comparingByValue()) .map(Map.Entry::getKey); 以允许列表中没有项目的情况,在这种情况下您将获得min。或者,如果列表为空,您可以在末尾添加Optional次调用以返回另一个empty

Map.Entry的使用有点不寻常(即不将其放在地图中)。有些orElse类的库可以完成同样的工作,或者你可以自己创建。

答案 2 :(得分:1)

等待时我会发布我目前正在考虑的内容:

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)

答案 3 :(得分:0)

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的预期用法,因此不太理想。