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.

链接地址: http://www.djcxy.com/p/87636.html

上一篇: 冗余一致性的用户协议PFSubclassing(Swift 2.0)

下一篇: 流中的地图元素对应于帖子