Unused Stream Pipeline

ID

java.intermediate_stream_methods_left_unused

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

Java

Tags

reliability

Description

Reports a statement whose whole effect is a Stream intermediate operation (map, filter, peek, sorted, distinct, limit, skip, flatMap) with nothing chained after it to run the pipeline.

Rationale

A Stream pipeline is lazy: none of its intermediate operations do any work until a terminal operation (forEach, collect, count, toList, …​) is called. A statement that ends on an intermediate operation therefore has no effect at all — any lambda passed to it never executes:

List<String> names = ...;
names.stream().map(String::trim); // nothing happens: the pipeline is built and immediately discarded

This usually means a terminal operation was forgotten, or the developer expected map/peek to run the side effect eagerly the way a plain loop would.

Remediation

Add the terminal operation the pipeline was meant to end with:

List<String> names = ...;
List<String> trimmed = names.stream().map(String::trim).collect(java.util.stream.Collectors.toList());

What is not reported

Only the statement’s outermost call is checked: a pipeline followed by a terminal operation (forEach, collect, …​), or whose result is assigned or returned rather than discarded, is not reported.

The receiver’s type must resolve to java.util.stream.. filter, map and peek are ordinary vocabulary well outside Stream — ByteBuffer.limit(int), InputStream.skip(long), a query builder’s filter(…​) — so matching on the method name alone produced 261 findings on unrelated types across ten projects. someOptional.filter(…​) left as a bare statement is therefore *not reported: the result is discarded there too, but Optional is not lazy, so the lambda does run and it is not the defect this rule names.

Demanding a resolved receiver costs two deliberate blind spots: a stream handed back by a project method the model cannot type, and a pipeline held in a var local. The chain parser types a receiver from its declaration, and var puts the type in the initializer, so var p = items.stream(); p.map(..); is silent while the same code written Stream<String> p = …​ is reported.

© 2026 Xygeni. All rights reserved