Replace Nested Loops with flatMap

Properties

Property Value
First seen in jSparrow version 2.2.0
Minimum Java version 8
Remediation cost 15 min

Description

Compound data structures similar to Collection<Collection<T>> are fairly common. This rule finds the nested invocations of Stream::forEach which are used to iterate over such data structures and replaces them with single invocations of Stream::flatMap. Using flatMap() not only makes the code more readable and but also allows for additional combinations with other Stream operations.

Benefits

Arguably, a flattened stream is easier to read.

Requirement & Tags

Requirements

Java 8

Code Changes

Iterating over nested collections

Pre

    List<List<User>> groups = findGroups();
    groups.forEach(group -> {
        group.forEach(user -> {
            sendAward(user);
        });
    });

Post

    List<List<User>> groups = findGroups();
    groups.stream()
        .flatMap(group -> group.stream())
        .forEach(user -> {
            sendAward(user);
        });

Nested Stream::forEach

Pre

orders.stream()
    .map(Order::getOrderItems)
    .forEach(items -> {
        items.forEach(item -> {
            Product product = item.getProduct();
            int quantity = item.getQuantity();
            add(product, quantity);
        });
    });

Post

orders.stream()
    .map(Order::getOrderItems)
    .flatMap(items -> items.stream())
    .forEach(item -> {
        Product product = item.getProduct();
        int quantity = item.getQuantity();
        add(product, quantity);
    });

Deep nested loops

Pre

    matrix3.stream().filter(row -> !row.isEmpty()).forEach(row -> {
        row.stream().filter(col -> !col.isEmpty()).forEach(col -> {
            col.stream().filter(cell -> !cell.isEmpty()).forEach(cell -> {
                cell.stream().filter(element -> !element.isEmpty()).map(element -> element.substring(0, 1)).forEach(element -> {
                    System.out.print(element);
                });
            });
        });
    });

Post

matrix3.stream().filter(row -> !row.isEmpty()).flatMap(row -> row.stream())
    .filter(col -> !col.isEmpty())
    .flatMap(col -> col.stream())
    .filter(cell -> !cell.isEmpty())
    .flatMap(cell -> cell.stream())
    .filter(element -> !element.isEmpty())
    .map(element -> element.substring(0, 1))
    .forEach(element -> {
        System.out.print(element);
    });

Automatic Application of This Rule

The automatic application of this rule is supported in the following jSparrow version: