How to collect objects to List based on properties of the class and then Map to HashMap in java 8 or 11?

Viewed 450

I have classes Similar to:

class Response {
    Source source;
    Target target;
}

class Source {
    Long sourceId;
    String sourceName;
}

class Target {
    Long targetId;
    String targetName;
}

In the Response, source(sourceId,sourceName) could be the same for different target object.

I have entry in Response with these 4 properties sourceId, sourceName, targetId, targetName. I can have sourceId, sourceName the same on multiple rows but targetId, targetName will always be different.

I want to group all the target objects into the List for which source is the same.

I have List of Response objects and I am trying to perform stream() operation on it something like:

Map<Source, List<Target>> res = results
        .stream()
        .collect(Collectors.groupingBy(
            Response::getSource)
        //collect to map

So that my final output JSON would look something like:

"Response": { 
    "Source":       {  
        "sourceId":       "100",   
        "sourceName":      "source1",    
    }
    "Target": [{//grouping target objects with same source
        "targetId":       "10",   
        "targetName":      "target1",   
    }, {
        "targetId":       "20",   
        "targetName":      "target2",   
    }]
}
2 Answers

Class Source needs to properly override hashCode and equals methods to be used as a key in Map.

However, another POJO should be implemented to contain the desired output:

@AllArgsConstructor
class Grouped {
    @JsonProperty("Source")
    Source source;

    @JsonProperty("Target")
    List<Target> target;
}

Then the list of responses may be converted as follows:

List<Response> responses; // set up the input list

List<Grouped> grouped = responses.stream()
    .collect(Collectors.groupingBy(
        Response::getSource,
        Collectors.mapping(Response::getTarget, Collectors.toList())
    )) // Map<Source, List<Target>> is built
    .entrySet()
    .stream() // Stream<Map.Entry<Source, List<Target>>>
    .map(e -> new Grouped(e.getKey(), e.getValue()))
    .collect(Collectors.toList());

I'm going to assume that the sourceName is String (Due to the json)

Does it have to be a stream?

It can probably be done pretty easily like the following:

Map<Source,List<Target>> res= new HashMap<>();
for(Response response : results){
        List<Target> sourceTarget = res.computeIfAbsent(response.source, list -> new ArrayList<>());
        sourceTarget.add(response.target);
    }
Related