是否有更优雅的方法从流中选择max或min方法?

时间:2015-11-22 19:32:05

标签: java java-8 java-stream

我希望根据某些条件从列表中获取最小或最大项目。 下面的摘录总结了我的工作:

Optional<Person> target;
if(condition){
    target = detailedList.stream().min((d1, d2) -> d1.age() + d2.age());
} else {
    target = detailedList.stream().max((d1, d2) -> d1.age() + d2.age());
}

它有效,但非常冗长。本质上,我想在流中选择方法 min max 。有更好的方法吗?

4 个答案:

答案 0 :(得分:8)

这是另一种方式:

Comparator<Person> comp = Comparator.comparingInt(Person::age);
BinaryOperator<Person> op 
    = condition ? BinaryOperator.minBy(comp) : BinaryOperator.maxBy(comp);

Optional<Person> target = list.stream().reduce(op);

答案 1 :(得分:4)

我建议让你的意图最清晰的方法是:

Comparator<Person> ageComparator = Comparator.comparingInt(Person::age);
Optional<Person> target = condition ? 
    list.stream().min(ageComparator) : list.stream().max(ageComparator);

答案 2 :(得分:2)

你可以这样做:

Comparator<Person> c = Comparator.comparingInt(Person::age);
Optional<Person> target = detailedList.stream().min(condition ? c : c.reversed());

它更短,但我个人认为将minreversed结合起来找到最大值有点像黑客。我会坚持原来的。

答案 3 :(得分:1)

另一种解决方案是使用相同的收集步骤计算最小值和最大值。为此,我们可以从IntSummaryStatistics类激发自己,并构建一个ObjectSummaryStatistics来保存最小值和最大值。

对于您的特定用例,此解决方案可能太多了。只有当你发现自己做了很多事情时,这可能会很方便。

min和max初始化为null,并在每次接受元素或进行合并时更新。我使用Optional作为返回值,以便与API为常见的minmax操作返回的内容保持一致。

public class ObjectSummaryStatistics<T> implements Consumer<T> {

    private long count;
    private T min;
    private T max;
    private Comparator<T> comparator;

    public ObjectSummaryStatistics(Comparator<T> comparator) {
        this.comparator = Objects.requireNonNull(comparator, "A comparator must be set");
    }

    @Override
    public void accept(T t) {
        min = count == 0 ? t : min(min, t);
        max = count == 0 ? t : max(max, t);
        count++;
    }

    public void combine(ObjectSummaryStatistics<T> other) {
        if (other.comparator != comparator) {
            throw new IllegalArgumentException("Can't combine with a summary statistics having a different comparator");
        }
        if (count == 0) {
            min = other.min;
            max = other.max;
        } else if (other.count > 0) {
            min = min(min, other.min);
            max = max(max, other.max);
        }
        count += other.count;
    }

    public Optional<T> getMin() {
        return Optional.ofNullable(min);
    }

    public Optional<T> getMax() {
        return Optional.ofNullable(max);
    }

    public long getCount() {
        return count;
    }

    private T min(T t1, T t2) {
        return comparator.compare(t1, t2) <= 0 ? t1 : t2;
    }

    private T max(T t1, T t2) {
        return comparator.compare(t1, t2) >= 0 ? t1 : t2;
    }

}

然后你可以构建一个收集到这个类的Collector

public static <T> Collector<T, ?, ObjectSummaryStatistics<T>> summarizing(Comparator<T> comparator) {
    return Collector.of(
                () -> new ObjectSummaryStatistics<T>(comparator), 
                ObjectSummaryStatistics::accept,
                (s1, s2) -> { s1.combine(s2); return s1; }
           );
}

最后,您的代码变为:

ObjectSummaryStatistics<Person> s = detailedList.stream().collect(summarizing((d1, d2) -> d1.age() + d2.age()));
System.out.println(s.getMin());
System.out.println(s.getMax());

旁注:你问题中的比较者不具有传递性,因此无法正常使用