Группировка потоков для меня ясна, когда речь идет о свойствах объектов, которые создают поток, но о том, как группировать объекты по условию, которое зависит от другого списка. Ниже приведен код с вложенными циклами, который я хотел бы преобразовать в потоковое решение.
public class Main {
public static void main(String[] args) {
LocalDate n = LocalDate.from(LocalDate.now());
List<LocalDate> groupingDates = Arrays.asList(n, n.plusDays(10), n.plusDays(20));
List<Item> itemsToBeGrouped = Arrays.asList(
//should go to group labeled by "n"
new Item(n, n.plusDays(1)),
new Item(n.minusDays(5), n.plusDays(7)),
//should go to group labeled by "n.plusDays(10)"
new Item(n.plusDays(5), n.plusDays(11)),
//should go to group labeled by "n.plusDays(20)"
new Item(n.plusDays(15), n.plusDays(20)));
Map<LocalDate, List<Item>> groupedItems = new LinkedHashMap<>();
for(Item i : itemsToBeGrouped) {
for (LocalDate date : groupingDates) {
if (isActiveOnDate(i, date)) {
if (!groupedItems.containsKey(date)) {
groupedItems.put(date, new ArrayList<>());
}
groupedItems.get(date).add(i);
}
}
}
System.out.println(groupedItems);
}
static boolean isActiveOnDate(Item item, LocalDate date) {
return !item.start.isAfter(date) && !item.end.isBefore(date);
}
}
public class Item{
public LocalDate start;
public LocalDate end;
public Item(LocalDate start, LocalDate end) {
this.start = start;
this.end = end;
}
@Override
public String toString() {
return "Item{" +
"start = " + start +
", end = " + end +
'}';
}
}
Сделайте то же самое в лямбде groupingBy
, как и в for-loop
, выберите первую дату, которой соответствует элемент, а затем используйте ее в качестве ключа:
Map<LocalDate, List<Item>> groupedItems = itemsToBeGrouped.stream()
.collect(Collectors.groupingBy(i -> groupingDates.stream()
.filter(d -> isActiveOnDate(i, d))
.findFirst() // Optional<Date>
.get() // will throw if nothing matches
));
Я рекомендую findFirst()
вместо findAny()
.
Что вы имеете в виду под «несогласованными данными»? любой пример?
@Nikolas nevermind, я прочитал что-то в коде OP, которого даже нет. findFirst
следует предпочесть для единообразия. Так что вы совершенно правы, я редактировал :)
Map<LocalDate, List<Item>> groupedItems2 = itemsToBeGrouped.stream()
.flatMap(x -> groupingDates.stream()
.filter(y -> isActiveOnDate(x, y))
.map(y -> new SimpleEntry<>(x, y)))
.collect(Collectors.groupingBy(
SimpleEntry::getValue,
LinkedHashMap::new,
Collectors.mapping(Entry::getKey, Collectors.toList())));
Где твои проблемы? Вы можете просто использовать
Collectors.groupingBy
с подходящим ключом (что означает передачу функции, которая выполняет итерацию по этим датам, как это делаете вы ...)