Traditional for or foreach loops that filter elements, transform them, or collect results into a collection can be more clearly expressed using functional collection processing APIs that provide declarative iteration constructs.

Why is this an issue?

Modern programming languages provide declarative, functional interfaces for processing collections. Traditional imperative loops that perform filtering, mapping, or collecting operations can be harder to read and understand at a glance because:

Common patterns that benefit from declarative collection processing include:

Declarative collection processing expresses these operations more clearly by:

While the traditional loop and declarative version are functionally equivalent, the declarative version communicates intent more directly, making the code easier to maintain and understand.

In Java, this declarative approach was introduced in Java 8 through the Stream API.

What is the potential impact?

This issue affects code maintainability and readability. While it doesn’t introduce bugs or security vulnerabilities, converting imperative iteration constructs to modern functional collection processing APIs:

The impact is primarily on developer productivity - code that clearly expresses intent is faster to understand, review, and modify.

How to fix it

Replace the traditional loop with a stream pipeline. Start with collection.stream(), then chain the appropriate intermediate operations (filter, map), and finish with a terminal operation (collect, findFirst, etc.).

Code examples

Noncompliant code example

List<String> result = new ArrayList<>();
for (Item item : items) {
    if (item.isActive()) { // Noncompliant
        result.add(item.getName());
    }
}

Compliant solution

List<String> result = items.stream()
    .filter(Item::isActive)
    .map(Item::getName)
    .collect(Collectors.toList());

Resources

Documentation