Java Streams – 如何按值分组并找到每组的最小值和最大值? [英] Java Streams – How to group by value and find min and max value of each group?
问题描述
以我的示例为例,拥有汽车对象并根据模型(分组依据)发现最小和最大价格值.
For my example, having car object and found that min and max price value based on model (group by).
List<Car> carsDetails = UserDB.getCarsDetails();
Map<String, DoubleSummaryStatistics> 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
但我找不到哪些汽车对象具有最高和最低价格.我该怎么做?
But I couldn't find which car objects have max and min price. How can I do that?
推荐答案
如果您对每组只有一个 Car
感兴趣,您可以使用,例如
If you were interested in only one Car
per group, you could use, e.g.
Map<String, Car> 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));
但既然你想要最贵和最便宜的,你需要这样的东西:
But since you want the most expensive and the cheapest, you need something like this:
Map<String, List<Car>> 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()<l2.get(1).getPrice()? l2: l1).get(1))));
mostExpensivesAndCheapest.forEach((make,cars) -> System.out.println(make
+" cheapest: "+cars.get(0)+" most expensive: "+cars.get(1)));
由于没有与 DoubleSummaryStatistics
等效的通用统计对象,因此该解决方案会带来一些不便.如果这种情况发生不止一次,那么值得用这样的类来填补空白:
This solution bears a bit of inconvenience due to the fact that there is no generic statistics object equivalent to DoubleSummaryStatistics
. If this happens more than once, it’s worth filling the gap with a class like this:
/**
* Like {@code DoubleSummaryStatistics}, {@code IntSummaryStatistics}, and
* {@code LongSummaryStatistics}, but for an arbitrary type {@code T}.
*/
public class SummaryStatistics<T> implements Consumer<T> {
/**
* Collect to a {@code SummaryStatistics} for natural order.
*/
public static <T extends Comparable<? super T>> Collector<T,?,SummaryStatistics<T>>
statistics() {
return statistics(Comparator.<T>naturalOrder());
}
/**
* Collect to a {@code SummaryStatistics} using the specified comparator.
*/
public static <T> Collector<T,?,SummaryStatistics<T>>
statistics(Comparator<T> comparator) {
Objects.requireNonNull(comparator);
return Collector.of(() -> new SummaryStatistics<>(comparator),
SummaryStatistics::accept, SummaryStatistics::merge);
}
private final Comparator<T> c;
private T min, max;
private long count;
public SummaryStatistics(Comparator<T> 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<T> merge(SummaryStatistics<T> 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);
}
}
将此添加到您的代码库后,您可以像使用它
After adding this to your code base, you may use it like
Map<String, SummaryStatistics<Car>> 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)
.
If getPrice
returns double
, it may be more efficient to use Comparator.comparingDouble(Car::getPrice)
instead of Comparator.comparing(Car::getPrice)
.
这篇关于Java Streams – 如何按值分组并找到每组的最小值和最大值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!