Home > Software design >  JAVA A list of objects with another list to Map
JAVA A list of objects with another list to Map

Time:11-11

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())
    ));
  • Related