简体   繁体   English

使用Java流的列表中列表的平均特定值

[英]Average specific values from a list within a list using Java stream

Can't figure out how to read out average weight of animals in a list for a specific building. 无法弄清楚如何读取特定建筑物列表中动物的平均体重。

I've written another method that gets the names of the animals per kind of animal, so I know my persistency is working. 我编写了另一种获取每种动物的动物名称的方法,所以我知道我的持久性正在发挥作用。

Zoo: 动物园:

public class Zoo {
    private List<Animal> animals;
    private List<Building> buildings;

    public Zoo() {
        this.animals = new PersistencyController().giveAnimals();
        this.gebouwen = new PersistencyController().giveBuildings();
    }

 public List<Animal> giveAnimalsByKind(String kindName) {
        return animals.stream().filter(animal -> animal.getKind().getName() == kindName).sorted(Comparator.comparing(Animal::getWeight)).collect(Collectors.toList());
    }

    public double giveAvgWeightForAnimalsInBuilding(String buildingName) {
        return animals.stream().collect(Collectors.averagingDouble(Animal::getWeight));
    }
}

Animal: 动物:

public class Animal implements Serializable {

    private int nr;
    private String name;
    private double weight;
    private Kind kind;

    public Animal(int nr, String name, double weight, Kind kind) {
        this.nr = nr;
        this.name = name;
        this.weight= weight;
        this.kind= kind;
    }

    public double getWeight() {
        return weight;
    }

    public void setWeight(double weight) {
        this.weight = weight;
    }
}

Building: 建造:

public class Building{

    private String naam;
    private int capaciteit;
    private final List<Animal> animals = new ArrayList<>();

    public Building(String naam, int capaciteit) {
        this.naam = naam;
        this.capaciteit = capaciteit;
    }
}

I want to get the average weight of the animals per building, so the idea is I pass a building's name. 我想获取每个建筑物中动物的平均体重,所以我的想法是传递建筑物的名称。 Every Building object stores a list of animals, and I need the building object because it stores the name of the building, but I can't figure out how to access it using streams. 每个建筑物对象都存储有动物列表,我需要建筑物对象,因为它存储建筑物的名称,但是我不知道如何使用流访问它。 I know that in the code I posted, I'm not actually using buildingName yet, but that's because I can't even get the average weight of the animals normally. 我知道在我发布的代码中,我实际上还没有使用buildingName,但这是因为我什至无法正常获得动物的平均体重。 weight is stored as a double in Animal and my persistency is working, as I've tested this in other ways. 重量存储为动物中的两倍,并且我的持久性正在发挥作用,因为我已经通过其他方式对此进行了测试。 Anyone experienced with streams explaining how to filter by building and get this average weight out would be much appreciated. 凡是具有流技术经验的人都可以解释如何通过构建进行过滤并获得该平均权重。

If you have the liberty of using Java9, I would suggest you using the flatMapping collector to get this thing done. 如果您可以自由使用Java9,建议您使用flatMapping收集器来完成此操作。 Here's how it looks. 这是它的外观。

Map<String, Double> avgWeightByBuildingName = buildings.stream()
    .collect(Collectors.groupingBy(Building::getName,
        Collectors.flatMapping(b -> b.getAnimals().stream(), 
            Collectors.averagingDouble(Animal::getWeight))));

However here's the java8 solution which looks a bit more verbose compared to the former. 但是,这是java8解决方案,与前一种解决方案相比,它看上去更加冗长。

Map<String, Double> avgWeightByBuildingName = buildings.stream()
    .flatMap(b -> b.getAnimals().stream()
        .map(a -> new AbstractMap.SimpleEntry<>(b.getName(), a.getWeight())))
    .collect(Collectors.groupingBy(Map.Entry::getKey, 
        Collectors.averagingDouble(Map.Entry::getValue)));

Alternatively consider writing your own flatMapping collector by following this answer . 或者,考虑遵循此答案来编写自己的flatMapping收集器。 Here's one such implementation that I came up with after looking at the above answer and the JDK 9 source code. 在看完以上答案和JDK 9源代码之后,我想到了这种实现。

static <T, U, A, R> Collector<T, ?, R> flatMapping(Function<? super T,
            ? extends Stream<? extends U>> mapper, Collector<? super U, A, R> downstream) {
    BiConsumer<A, ? super U> downstreamAccumulator = downstream.accumulator();
    return Collector.of(downstream.supplier(), (r, t) -> {
        try (Stream<? extends U> result = mapper.apply(t)) {
            result.sequential().forEach(u -> downstreamAccumulator.accept(r, u));
        }
    }, downstream.combiner(), downstream.finisher(), 
            downstream.characteristics().toArray(new Characteristics[0]));
}

Moreover this has a straight-forward migration strategy for newer Java versions as stated in the below comment. 此外,对于以下较新的Java版本,这具有简单的迁移策略,如下面的注释所述。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM