DEV Community

yangbongsoo
yangbongsoo

Posted on

How to handle a Map using Java stream

Let's look at the code below. I create a Product class that contains a list of Items.

@Value
@Builder
public class Product {
    long productId;

    List<Item> items;

    @Value
    @Builder
    public static class Item {
        long itemId;
    }
}
Enter fullscreen mode Exit fullscreen mode

And then I get the product list using an external API. In order to handle item objects, I use a nested for loop.

I make itemsByItemId Map object. Key is itemId and value is item object.

public class Main {
    public static void main(String[] args) {
        // get product list using an external api
        ApiClient apiClient = new ApiClient();
        List<Product> products = apiClient.getProducts().collectList().block();

        Map<Long, Item> itemsByItemId = new HashMap<>();
        for (Product product : products) {
            for (Item item : product.getItems()) {
                itemsByItemId.put(item.getItemId(), item);
            }
        }

        System.out.println(itemsByItemId);
    }
}
Enter fullscreen mode Exit fullscreen mode

However I want to use java stream api. Let's look at the code below. It looks complicated at a glance.

For each item, we create and merge Maps using Map.of, and since we need to merge again at the product level, we incorporate flatMap(map -> map.entrySet().stream()) and collect(Collectors.toMap) twice each.

public class Main {
  public static void main(String[] args) {
    // get product list using an external api
    ApiClient apiClient = new ApiClient();
    List<Product> products = apiClient.getProducts().collectList().block();

    Map<Long, Item> itemsByItemId = products.stream() // Stream<Product>
      .map(product ->
        product.getItems().stream() // Stream<Item>
          .map(item -> 
            Map.of(item.getItemId(), item)) // Stream<Map<Long, Item>>
            .flatMap(map -> map.entrySet().stream()) // Stream<Entry<Long, Item>>
            .collect(Collectors.toMap(
              Entry::getKey,
              Entry::getValue,
              (item1, item2) -> item2
          )
        )
      ) // Stream<Map<Long, Item>>
      .flatMap(map -> map.entrySet().stream()) // Stream<Entry<Long, Item>>
      .collect(Collectors.toMap(
        Entry::getKey,
        Entry::getValue,
        (item1, item2) -> item2
      )
    );

    System.out.println(itemsByItemId);
  }
}
Enter fullscreen mode Exit fullscreen mode

This is a better solution code. I create item lists using flatMap. .flatMap(product -> product.getItems().stream()) And then, I only need to create them once with Map.of.

public class Main {
  public static void main(String[] args) {
    // get product list using an external api
    ApiClient apiClient = new ApiClient();
    List<Product> products = apiClient.getProducts().collectList().block();

    Map<Long, Item> itemsByItemId = products.stream() // Stream<Product>
      .flatMap(product -> product.getItems().stream()) // Stream<Item>
      .map(item -> Map.of(item.getItemId(), item)) // Stream<Map<Long, Item>>
      .flatMap(map -> map.entrySet().stream()) // Stream<Entry<Long, Item>>
      .collect(Collectors.toMap(
          Entry::getKey,
          Entry::getValue,
          (item1, item2) -> item2
        )
      );

    System.out.println(itemsByItemId);  
  }
}
Enter fullscreen mode Exit fullscreen mode

However, in reality, since collect(Collectors.toMap) is used to create the Map, there's no need for me to manually work with Map.of. Ultimately, it resulted in much simpler code as shown below.

public class Main {
  public static void main(String[] args) {
    // get product list using an external api
    ApiClient apiClient = new ApiClient();
    List<Product> products = apiClient.getProducts().collectList().block();

    Map<Long, Item> itemsByItemId = products.stream() // Stream<Product>
      .flatMap(product -> product.getItems().stream()) // Stream<Item>
      .collect(Collectors.toMap(Item::getItemId, Function.identity()));

    System.out.println(itemsByItemId);  
  }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)