Как преобразовать вложенный foreach с использованием Stream API?
Как можно преобразовать следующий код с вложенным циклом foreach с использованием Stream API?
private static final int calculateMaximumSeedsInOneBox() {
int maximum = 0;
int currentGranateSeeds = 0;
int currentBoxSeeds = 0;
for (Box box : boxesWithPomegrantes) {
for (Pomegranate pomegranate : box) {
currentGranateSeeds = pomegranate.getSeeds().size();
}
currentBoxSeeds += currentGranateSeeds;
if (currentBoxSeeds > maximum) {
maximum = currentBoxSeeds;
}
}
return maximum;
}
private static List<String> findNamesOfBoxesWithMaximumSeeds() {
List<String> namesOfBoxes = new ArrayList<>();
int maximum = calculateMaximumSeedsInOneBox();
int currentGranateSeeds = 0;
int currentBoxSeeds = 0;
for (Box box : boxesWithPomegrantes) {
for (Pomegranate pomegranate : box) {
currentGranateSeeds = pomegranate.getSeeds().size();
}
currentBoxSeeds += currentGranateSeeds;
if (currentBoxSeeds == maximum) {
namesOfBoxes.add(box.getName());
}
}
return namesOfBoxes;
}
Ответы (2 шт):
Для корректного поиска максимума гранатовых зернышек в одной коробке первый метод следует переписать следующим образом, с учётом того, что существует следующие иерархия классов:
Boxявляется коллекциейPomegranate(напримерBox extends ArrayList<Pomegranate>), и имеет полеnameс соответствующим геттеромgetNamePomegranateсодержит коллекциюSeed
Тогда максимум можно найти следующим образом:
private static final int calculateMaximumSeedsInOneBox(Collection<Box> boxesWithPomegranates) {
return boxesWithPomegranates.stream() // Stream<Box>
.mapToInt(b -> b.stream() // Stream<Pomegranate>
.mapToInt(g -> g.getSeeds().size()) // IntStream seeds size
.sum()
)
.max() // OptionalInt
.orElse(-1); // для пустого ввода
}
Соответственно, для определения названий коробок, содержащих максимум зёрнышек, нужно применить Stream::filter:
private static List<String> findNamesOfBoxesWithMaximumSeeds(Collection<Box> boxesWithPomegranates) {
int max = calculateMaximumSeedsInOneBox(boxesWithPomegranates);
return boxesWithPomegranates.stream() // Stream<Box>
.filter(b -> max == b.stream()
.mapToInt(g -> g.getSeeds().size())
.sum()
)
.map(Box::getName) // Stream<String> названия коробок
.collect(Collectors.toList());
}
Альтернативный способ для получения списка названий коробок за один проход: сразу построить отсортированную мапу с ключом количеством зёрнышек и значением -- списком названий, и взять последний элемент такой мапы.
Также можно упростить код за счёт вынесения подсчёта количеств зёрнышек в класс Box:
// class Box
public int totalSeedCount() {
return this.stream().mapToInt(g -> g.getSeeds().size()).sum();
}
private static List<String> namesOfBoxesWithMaxSeeds(Collection<Box> boxesWithPomegranates) {
return boxesWithPomegranates.stream()
.collect(Collectors.groupingBy(
Box::totalSeedCount, // ключ - сумма зёрнышек в коробке
TreeMap::new, // отсортированная мапа по возрастанию
Collectors.mapping(Box::getName, Collectors.toList())
))
.lastEntry() // Map.Entry<Integer, List<String>> макс.ключ
.getValue(); // List<String>
}
Вот такое решение получилось у меня, и оно работает правильно:
private static final int calculateMaximumSeedsInOneBoxStream() {
Optional<Box> res = boxesWithPomegrantes.stream()
.collect(
Collectors.maxBy(Comparator.comparing(Box::getQuantytyOfSeedsInCurrentBox)));
return res.get().getQuantytyOfSeedsInCurrentBox();
}
private static List<String> findNamesOfBoxesWithMaximumSeedsStream() {
int maximum = calculateMaximumSeedsInOneBox();
Map<Boolean, List<Box>> map = boxesWithPomegrantes.stream()
.collect(
Collectors.partitioningBy(
s -> s.getQuantytyOfSeedsInCurrentBox() == maximum));
List<String> namesOfBoxes = map.entrySet().stream()
.filter(m -> m.getKey().booleanValue() == true)
.map(Map.Entry::getValue)
.flatMap(l -> l.stream())
.map(b -> b.getName())
.collect(Collectors.toList());
return namesOfBoxes;
}