Sachin HR
Sachin HR

Reputation: 460

Iterating over a List of nested objects and storing them into a HashMap using Stream API

I am trying to iterate over nested java object and from nested object I'm trying to populate a map using Java 8.

Here is the list and structure of objects

IssueTypeDto.java

public class IssueTypeDto {
    private String id;
    private String name;
    private List<CustomFieldDto> customFields;
}

CustomFieldDto.java

    private String id;
    private TypeDto type;

I am getting List of IssueTypeDto as response. Each IssueTypeDto object can have a list of CustomFieldDto objects. My requirement is I need to iterate over a list of IssueTypeDto objects and for each CustomFieldDto object I need to get id and the corresponding TypeDto object and insert it into a map as key and value. Here is what I am trying to do currently

Map<String, TypeDto> map = issueTypes.stream()
    .flatMap(issueType->issueType.getCustomFields().stream()
                .collect(Collectors.toMap(
                            CustomFieldDto::getId,
                            CustomFieldDto::getType)));

But i am getting compile time error as Type mismatch:

cannot convert from Stream<Object> to Map<String,TypeDto>"

I am new to Java 8 streams. So I am not able to figure out the issue. Any help would be appreciated.

Upvotes: 1

Views: 1235

Answers (2)

Stanislau Kozel
Stanislau Kozel

Reputation: 166

Let's know the general concepts. You want to transform one object to another one. It can be 1 to 1 relationship or 1 to many. In case of 1 to 1 you need to use .map. 1 to many - .flatMap. So in other words map convert Object A to Object B. flatMap convert Object A to Stream of Object B. You was near you missed ')'.

Your case should be like this:

issueTypes.stream()
.flatMap(issueType -> issueType.getCustomFields().stream())
.collect(Collectors.toMap(CustomFieldDto::getId CustomFieldDto::getType));

Also please be sure that there are no duplicates on keys of different objects, otherwise Collectors.toMap() will throw exception due to key duplication.

Upvotes: 2

Alexander Ivanchenko
Alexander Ivanchenko

Reputation: 28968

The code you've provided does not compile for two reasons:

  • flatMap() is an intermediate operation which expects a function that returns a stream as an argument. Instead, you've created a nested stream that generates a map. Therefore, your function doesn't match what flatMap() expects.

  • Your stream pipeline lacks the terminal operation, therefore this assignment is incorrect

Map<String, TypeDto> map = issueTypes.stream().flatMap();

There's a map on the left side and a stream on the right side (because flatMap() is an intermediate operation, returns you stream).

To fix it, you need to make the function passed into the flatMap() return a stream, and add a terminal operation to the pipeline, i.e. produce the result from the main stream by moving collect() out from the flatMap().

That's how it can be done by using Collectors.toMap() (in the comments you've mentioned that there could be duplicated ids ):

Map<String, TypeDto> map = issueTypes.stream()
    .flatMap(issueType -> issueType.getCustomFields().stream())
    .collect(Collectors.toMap(
        CustomFieldDto::getId,   // mapping a key
        CustomFieldDto::getType, // mapping a value
        (left, right) -> left    // resolving duplicates - preserve the first encountered value
    ));

Another way of handling duplicates is preserve all of them by storing values mapped to the same key into a list, which can be done with Collectors.groupingBy():

 Map<String, List<TypeDto>> map = issueTypes.stream()
    .flatMap(issueType -> issueType.getCustomFields().stream())
    .collect(Collectors.groupingBy(CustomFieldDto::getId));

Upvotes: 2

Related Questions