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

时间:2019-08-20 16:04:19

标签: java lambda java-8 java-stream collectors

无法弄清楚如何读取特定建筑物列表中动物的平均体重。

我编写了另一种获取每种动物的动物名称的方法,所以我知道我的持久性正在发挥作用。

动物园:

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));
    }
}

动物:

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;
    }
}

建筑物:

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;
    }
}

我想获取每个建筑物中动物的平均体重,所以我的想法是传递建筑物的名称。每个建筑物对象都存储有动物列表,我需要建筑物对象,因为它存储建筑物的名称,但是我不知道如何使用流访问它。我知道在我发布的代码中,我实际上还没有使用buildingName,但这是因为我什至无法正常获得动物的平均体重。重量存储为动物中的两倍,并且我的持久性正在发挥作用,因为我已经通过其他方式对此进行了测试。精通流的任何人都将解释如何通过构建进行过滤并获得此平均权重,这一点将不胜感激。

1 个答案:

答案 0 :(得分:6)

如果您有使用Java9的自由,我建议您使用flatMapping收集器来完成此工作。看起来就是这样。

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

不过,这是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)));

或者考虑遵循此answer来编写自己的flatMapping收藏家。看完以上答案和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]));
}

此外,对于以下较新的Java版本,这具有简单明了的迁移策略,如下面的注释所述。