Java 8 – Stream – 按值分组并查找该对象的最小值和最大值

对于我的例子,拥有汽车对象并发现基于模型的最小和最大价格值(分组依据)。

List carsDetails = UserDB.getCarsDetails(); Map collect4 = carsDetails.stream() .collect(Collectors.groupingBy(Car::getMake, Collectors.summarizingDouble(Car::getPrice))); collect4.entrySet().forEach(e->System.out.println(e.getKey()+" "+e.getValue().getMax()+" "+e.getValue().getMin())); output : Lexus 94837.79 17569.59 Subaru 96583.25 8498.41 Chevrolet 99892.59 6861.85 

但我找不到哪个车对象有最高和最低价格。 我怎样才能做到这一点?

如果您对每组只有一辆Car感兴趣,可以使用,例如

 Map mostExpensives = carsDetails.stream() .collect(Collectors.toMap(Car::getMake, Function.identity(), BinaryOperator.maxBy(Comparator.comparing(Car::getPrice)))); mostExpensives.forEach((make,car) -> System.out.println(make+" "+car)); 

但既然你想要最便宜和最便宜的,你需要这样的东西:

 Map> mostExpensivesAndCheapest = carsDetails.stream() .collect(Collectors.toMap(Car::getMake, car -> Arrays.asList(car, car), (l1,l2) -> Arrays.asList( (l1.get(0).getPrice()>l2.get(0).getPrice()? l2: l1).get(0), (l1.get(1).getPrice() System.out.println(make +" cheapest: "+cars.get(0)+" most expensive: "+cars.get(1))); 

由于没有与DoubleSummaryStatistics等效的通用统计对象,因此该解决方案带来一些不便。 如果这种情况不止一次发生,那么值得用这样的类填补空白:

 /** * Like {@code DoubleSummaryStatistics}, {@code IntSummaryStatistics}, and * {@code LongSummaryStatistics}, but for an arbitrary type {@code T}. */ public class SummaryStatistics implements Consumer { /** * Collect to a {@code SummaryStatistics} for natural order. */ public static > Collector> statistics() { return statistics(Comparator.naturalOrder()); } /** * Collect to a {@code SummaryStatistics} using the specified comparator. */ public static  Collector> statistics(Comparator comparator) { Objects.requireNonNull(comparator); return Collector.of(() -> new SummaryStatistics<>(comparator), SummaryStatistics::accept, SummaryStatistics::merge); } private final Comparator c; private T min, max; private long count; public SummaryStatistics(Comparator comparator) { c = Objects.requireNonNull(comparator); } public void accept(T t) { if(count == 0) { count = 1; min = t; max = t; } else { if(c.compare(min, t) > 0) min = t; if(c.compare(max, t) < 0) max = t; count++; } } public SummaryStatistics merge(SummaryStatistics s) { if(s.count > 0) { if(count == 0) { count = s.count; min = s.min; max = s.max; } else { if(c.compare(min, s.min) > 0) min = s.min; if(c.compare(max, s.max) < 0) max = s.max; count += s.count; } } return this; } public long getCount() { return count; } public T getMin() { return min; } public T getMax() { return max; } @Override public String toString() { return count == 0? "empty": (count+" elements between "+min+" and "+max); } } 

将此添加到您的代码库后,您可以使用它

 Map> mostExpensives = carsDetails.stream() .collect(Collectors.groupingBy(Car::getMake, SummaryStatistics.statistics(Comparator.comparing(Car::getPrice)))); mostExpensives.forEach((make,cars) -> System.out.println(make+": "+cars)); 

如果getPrice返回double ,则使用Comparator.comparingDouble(Car::getPrice)而不是Comparator.comparing(Car::getPrice)可能更有效。

我想提出一个解决方案(在我看来)是为了获得最大的可读性(这减少了例如此类代码的维护负担)。

它是基于Collector的 – 作为奖励 – 它可以与并行Stream一起使用。 它假定对象是非null。

 final class MinMaxFinder { private final Comparator comparator; MinMaxFinder(Comparator comparator) { this.comparator = comparator; } Collector> collector() { return Collector.of( MinMaxAccumulator::new, MinMaxAccumulator::add, MinMaxAccumulator::combine, MinMaxAccumulator::toResult ); } private class MinMaxAccumulator { T min = null; T max = null; MinMaxAccumulator() { } private boolean isEmpty() { return min == null; } void add(T item) { if (isEmpty()) { min = max = item; } else { updateMin(item); updateMax(item); } } MinMaxAccumulator combine(MinMaxAccumulator otherAcc) { if (isEmpty()) { return otherAcc; } if (!otherAcc.isEmpty()) { updateMin(otherAcc.min); updateMax(otherAcc.max); } return this; } private void updateMin(T item) { min = BinaryOperator.minBy(comparator).apply(min, item); } private void updateMax(T item) { max = BinaryOperator.maxBy(comparator).apply(max, item); } MinMaxResult toResult() { return new MinMaxResult<>(min, max); } } } 

结果持有者类似于类:

 public class MinMaxResult { private final T min; private final T max; public MinMaxResult(T min, T max) { this.min = min; this.max = max; } public T min() { return min; } public T max() { return max; } } 

用法:

 MinMaxFinder minMaxFinder = new MinMaxFinder<>(Comparator.comparing(Car::getPrice)); Map> minMaxResultMap = carsDetails.stream() .collect(Collectors.groupingBy(Car::getMake, minMaxFinder.collector()));