First, the mentioned POJO classes Pojo4 and Pojo3 need to implement hashCode and equals in order to be used as map keys as well as relevant getters for the properties.
The conversion should use common Collectors.groupingBy / Collectors.mapping collectors, and initial remapping to a plain container of two objects such as Pair is needed.
The example below uses Java 11 Map.entry as an intermediate container.
static Map<Pojo4, <Map<Pojo3, List<Pojo2>>>> convert(Stream<Pojo1> ones) {
return ones // Stream<Pojo1>
.map(Pojo1::getItem) // Stream<Pojo2>
.map(p2 -> Map.entry(p2, p2.getItem())) // Stream<Map.Entry<Pojo2, Pojo3>>
.collect(Collectors.groupingBy(
me -> me.getValue().getItem(), // key: Pojo4
Collectors.groupingBy(
me -> me.getValue(), // key: Pojo3
Collectors.mapping(
Map.Entry::getKey, Collectors.toList()
)
)
));
}
Similarly, sorted version may be implemented using Collectors.groupingBy with map supplier; additionally List<Pojo2> should be sorted using collectingAndThen.
Assuming that Pojo2, Pojo3, Pojo4 have getter getField for the property to sort, the following update is provided:
static Map<Pojo4, <Map<Pojo3, List<Pojo2>>>> convert(Stream<Pojo1> ones) {
return ones
.map(Pojo1::getItem) // Stream<Pojo2>
.map(p2 -> Map.entry(p2, p2.getItem())) // Stream<Map.Entry<Pojo2, Pojo3>>
.collect(Collectors.groupingBy(
me -> me.getValue().getItem(), // key: Pojo4
() -> new TreeMap(Comparator.comparing(Pojo4::getField)),
Collectors.groupingBy(
me -> me.getValue(), // key: Pojo3
() -> new TreeMap(Comparator.comparing(Pojo3::getField)),
Collectors.mapping(
Map.Entry::getKey, Collectors.collectingAndThen(
Collectors.toList(), // List<Pojo2>
list -> {
list.sort(Comparator.comparing(Pojo2::getField));
return list;
}
)
)
)
));
}
Disclaimer: the code samples above have not been tested, so additional tweaking may be needed.