Java Streams–;如何按值分组并找到每组的最小值和最大值?

Java Streams–;如何按值分组并找到每组的最小值和最大值?,java,java-8,java-stream,Java,Java 8,Java Stream,在我的示例中,使用car对象并发现基于模型的最小和最大价格值(groupby) List carsDetails=UserDB.getCarsDetails(); Map collect4=carsDetails.stream() .collect(Collectors.groupingBy(Car::getMake,Collectors.summaringdouble(Car::getPrice)); collect4.entrySet().forEach(e->System.out.prin

在我的示例中,使用car对象并发现基于模型的最小和最大价格值(groupby)

List carsDetails=UserDB.getCarsDetails();
Map collect4=carsDetails.stream()
.collect(Collectors.groupingBy(Car::getMake,Collectors.summaringdouble(Car::getPrice));
collect4.entrySet().forEach(e->System.out.println(e.getKey()+“”+e.getValue().getMax()+“”+e.getValue().getMin());
输出:
雷克萨斯94837.79 17569.59
斯巴鲁96583.25 8498.41
雪佛兰99892.59 6861.85

但我找不到哪些汽车对象具有最高和最低价格。我该怎么做呢?

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

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));
将其添加到代码库后,您可以像

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));
Map mostExpensives=carsDetails.stream()
.collect(收集器).groupingBy(汽车::getMake,
统计(Comparator.comparing(Car::getPrice));
mostExpensives.forEach((make,cars)->System.out.println(make+“:“+cars));

如果
getPrice
返回
double
,使用
Comparator.comparingDouble(Car::getPrice)
而不是
Comparator可能会更有效。comparing(Car::getPrice)
我想提出一个(在我看来)力求最大可读性的解决方案(例如,减少此类代码的维护负担)

它基于
收集器
,因此作为一种额外的功能,它可以与并行
一起使用。它假定对象为非空

final class MinMaxFinder<T> {

    private final Comparator<T> comparator;

    MinMaxFinder(Comparator<T> comparator) {
        this.comparator = comparator;
    }

    Collector<T, ?, MinMaxResult<T>> 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<T> toResult() {
            return new MinMaxResult<>(min, max);
        }
    }
}
final类MinMaxFinder{
私人最终比较人;
MinMaxFinder(比较器比较器){
这个比较器=比较器;
}
收集器(){
返回收集器(
MinMaxAccumulator::新,
MinMaxAccumulator::添加,
联合收割机,
MinMaxAccumulator::toResult
);
}
私有类minmaxacculator{
T min=null;
T max=null;
MinMaxAccumulator(){
}
私有布尔isEmpty(){
返回min==null;
}
无效添加(T项){
if(isEmpty()){
最小值=最大值=项目;
}否则{
更新名(项目);
更新最大值(项目);
}
}
MinMaxAccounter联合收割机(MinMaxAccounter){
if(isEmpty()){
返回其他ACC;
}
如果(!otherAcc.isEmpty()){
更新最小值(其他最小值);
更新最大值(其他最大值);
}
归还这个;
}
私有无效更新名(T项){
min=BinaryOperator.minBy(比较器).apply(最小,项);
}
私有void updateMax(T项){
max=BinaryOperator.maxBy(比较器).apply(最大值,项);
}
MinMaxResult-toResult(){
返回新的MinMaxResult(最小值、最大值);
}
}
}
结果持有者值类似于类:

public class MinMaxResult<T> {
    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;
    }
}
公共类MinMaxResult{
私人决赛T分钟;
私人最终T最大值;
公共最小最大结果(T最小值,T最大值){
this.min=min;
this.max=max;
}
公营部门{
返回最小值;
}
公共T max(){
返回最大值;
}
}
用法:

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

这里有一个非常简洁的解决方案。它将所有
车辆
收集到一个
分类数据集中
,因此无需任何附加类即可工作

Map<String, SortedSet<Car>> grouped = carDetails.stream()
        .collect(groupingBy(Car::getMake, toCollection(
                () -> new TreeSet<>(comparingDouble(Car::getPrice)))));

grouped.forEach((make, cars) -> System.out.println(make
        + " cheapest: " + cars.first()
        + " most expensive: " + cars.last()));
Map group=cardetals.stream()
.collect(按汽车分组):getMake,toCollection(
()->新树集(比较双倍(Car::getPrice()())));
grouped.forEach((make,cars)->System.out.println(make
+“最便宜的:”+汽车。第一()
+“最贵的:”+汽车。最后一辆());

一个可能的缺点是性能,因为所有的
汽车
都是收集的,不仅仅是当前的最小值和最大值。但除非数据集非常大,否则我认为它不会明显。

但我找不到哪个汽车对象有最大和最小价格->你的意思是最大的价格差异吗?所以你有一个汽车列表,你想根据价格找到这些车的最大值和最小值?不要使用
DoubleSummary统计数据
只得到最小值和最大值@Eugene.是的。你是对的..根据车型找到这些车的最大值和最小值可能有点挑剔..>
Comparator.comparingDouble(…)
而不是
Comparator.comparing(…)
。我第一次从霍尔格那里学到这一点:)@Aominè当我确信
getPrice
返回
double
而不是
double
@LearnHadoop时,我会使用这个逻辑,正如我所说的,逻辑应该类似于数字统计对象,例如,因此,您可以在他们的文档中找到一些提示。不是数字,我们没有求和或求平均,因此它只维护
min
max
count
。基本上,收集器将为每个元素resp调用
accept
。与
groupingBy
组合时,针对组中的每个元素。只有使用并行计算,它才能调用
merge
方法来合并部分结果。@RavindraRanwala,这在问题中已经给出了。问题是这种方法没有提供什么。@TomaszLinkowski基于
列表
的解决方案主要是为了“我可以在不创建新类的情况下做到这一点”的分数…这接近于我的
汇总统计
方法,尽管我不会将结果复制到不可变的类型中。请注意,可以支持此类收集器的订购,即保证在出现连接的情况下,第一次遇到
public class MinMaxResult<T> {
    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<Car> minMaxFinder = new MinMaxFinder<>(Comparator.comparing(Car::getPrice));
Map<String, MinMaxResult<Car>> minMaxResultMap = carsDetails.stream()
            .collect(Collectors.groupingBy(Car::getMake, minMaxFinder.collector()));
Map<String, SortedSet<Car>> grouped = carDetails.stream()
        .collect(groupingBy(Car::getMake, toCollection(
                () -> new TreeSet<>(comparingDouble(Car::getPrice)))));

grouped.forEach((make, cars) -> System.out.println(make
        + " cheapest: " + cars.first()
        + " most expensive: " + cars.last()));