Given a class Object1
class Object1 {
private Object2 object2;
private List<Object3> object3s;
}
and a List<Object1>
cuurent implementation
Map<Object3, Object2> accountMap = new HashMap<>();
for (Object1 dto : accounts) {
dto.getObject3s()
.forEach(dto -> accountMap.put(dto, dto.getObject2()));
}
How do I create a Map<Object3, Object2>
preferably in a stream?
CodePudding user response:
I believe doing this in a stream would look something like this:
Map<Object3, Object2> map = list.stream()
.flatMap(o1 -> o1.object3s.stream().map(o3 -> Map.entry(o3, o1.object2)))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
That is, flatmap your stream of Object1
to a stream of entries of Object3
and Object2
; and then collect them to a map.
CodePudding user response:
To achieve this with streams, you need an auxiliary object, that would hold references to Object3
and Object2
.
A good practice is to use a Java 16 record
for that purpose, if you're using an earlier JDK version you can define a class
. Sure a quick and dirty option Map.Entry
will do as well, but it reduces the readability of code because methods getKey()
and getValue()
give no clue what they are returning, it would be even confusing if the source of the stream is entry set and there would be several kinds of entries in the pipeline.
Here's how such a record might look like:
public record Objects3And2(Object3 object3, Object2 object2) {}
In the stream, you need to flatten the data by creating a new instance of record Objects3And2
for every nested Object3
. For that, we can use either flatMap()
of mapMulti()
.
And to accumulate the data into a map we can make use of the collector `toMap()
Note that in this implementation (as well as in your code) values of duplicated Object3
would be overridden.
List<Object1> accounts = // initializing the list
Map<Object3, Object2> accountMap = accounts.stream()
.flatMap(o1 -> o1.getObject3s().stream()
.map(o3 -> new Objects3And2(o3, o1.getObject2()))
)
.collect(Collectors.toMap(
Objects3And2::object3, // generating a Key
Objects3And2::object2, // generating a Value
(l, r) -> r // resolving Duplicates
));
If you don't want to lose the data, you need a different structure of the resulting map and different collector, namely groupingBy()
in conjunction with mapping()
as its downstream.
List<Object1> accounts = // initializing the list
Map<Object3, List<Object2>> accountMap = accounts.stream()
.flatMap(o1 -> o1.getObject3s().stream()
.map(o3 -> new Objects3And2(o3, o1.getObject2()))
)
.collect(Collectors.groupingBy(
Objects3And2::object3,
Collectors.mapping(Objects3And2::object2,
Collectors.toList())
));