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. 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.
IntStream average() method in Java The average() method of the IntStream class in Java returns an OptionalDouble describing the arithmetic mean of elements of this stream, or an empty optional if this stream is empty. It gets the average of the elements of the stream.
If you have the liberty of using Java9, I would suggest you using the flatMapping
collector to get this thing done. 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.
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. Here's one such implementation that I came up with after looking at the above answer and the JDK 9 source code.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With