In Java 8 Streams, a stream pipeline is a sequence of operations that process a stream of elements in a functional and declarative manner. It consists of a source, zero or more intermediate operations, and a terminal operation. Chaining operations refers to the process of combining multiple intermediate operations in a fluent API style to transform and process data efficiently.
Stream Pipeline Overview
 stream pipeline processes data in three stages:
- Source: The origin of the data (e.g., a collection, array, or generator).
- Intermediate Operations: Transformations or filters applied to the stream (e.g., filter, map, sorted). These are lazy and produce a new stream.
- Terminal Operation: A final operation that produces a result or side effect (e.g., collect, forEach, reduce). This triggers the execution of the pipeline.
Key Characteristics
- Fluent API: Operations are chained using method calls (e.g., stream.filter(…).map(…).collect(…)), making the code concise and readable.
- Lazy Evaluation: Intermediate operations are not executed until a terminal operation is invoked.
- Single Pass: The pipeline processes elements in one pass, optimizing performance.
- Immutability: Streams do not modify the source data; they produce new results.
Chaining Operations
Chaining operations involves linking multiple intermediate operations to transform the stream progressively. Each intermediate operation returns a new Stream, allowing further operations to be appended.
Common Intermediate Operations
- filter(Predicate<T>): Keeps elements matching the predicate.
- map(Function<T, R>): Transforms each element into a new value.
- flatMap(Function<T, Stream<R>>): Flattens nested streams into a single stream.
- sorted() or sorted(Comparator<T>): Sorts elements.
- distinct(): Removes duplicates.
- limit(long maxSize): Restricts the number of elements.
- skip(long n): Skips the first n elements.
- peek(Consumer<T>): Performs an action on each element (useful for debugging).
Common Terminal Operations
- collect(Collector): Accumulates elements into a collection (e.g., toList(), groupingBy).
- forEach(Consumer): Applies an action to each element.
- reduce(BinaryOperator): Combines elements into a single result.
- count(): Returns the number of elements.
- findFirst() or findAny(): Returns an element (or Optional).
- anyMatch(Predicate), allMatch(Predicate), noneMatch(Predicate): Checks conditions.
How Stream Pipelines Work
- Source Creation: A stream is created from a source (e.g., List.stream(), Stream.of(), Arrays.stream()).
- Chaining Intermediate Operations: Operations like filter, map, etc., are chained to define the pipeline. These are lazy and build a pipeline definition without processing data.
- Terminal Operation: When a terminal operation is called, the pipeline is executed, processing elements from the source through all intermediate operations in one pass.
Examples of Stream Pipelines and Chaining
1. Basic Pipeline: Filter and Collect
Filter even numbers from a list and collect them into a new list:
import java.util.*; import java.util.stream.*; public class Main { public static void main(String[] args) { List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6); List<Integer> evenNumbers = numbers.stream() .filter(n -> n % 2 == 0) .collect(Collectors.toList()); System.out.println("Even numbers: " + evenNumbers); } } /* Even numbers: [2, 4, 6] */
2. Chaining Multiple Operations
Filter even numbers, double them, and sort in descending order:
List<Integer> numbers = Arrays.asList(1, 4, 2, 5, 3, 6);
List<Integer> result = numbers.stream()
.filter(n -> n % 2 == 0)
.map(n -> n * 2)
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
System.out.println("Processed numbers: " + result);
/*
Processed numbers: [12, 8, 4]
*/
Code language: PHP (php)
3. Pipeline with FlatMap
Flatten a list of lists and filter elements:
List<List<String>> listOfLists = Arrays.asList(
Arrays.asList("apple", "banana"),
Arrays.asList("cherry", "date"),
Arrays.asList("elderberry")
);
List<String> longWords = listOfLists.stream()
.flatMap(List::stream)
.filter(s -> s.length() > 5)
.collect(Collectors.toList());
System.out.println("Words longer than 5 characters: " + longWords);
/*
Words longer than 5 characters: [banana, cherry, elderberry]
*/
Code language: PHP (php)
4. Pipeline with Short-Circuiting
Find the first number greater than 3:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> first = numbers.stream()
.filter(n -> {
System.out.println("Filtering: " + n);
return n > 3;
})
.map(n -> {
System.out.println("Mapping: " + n);
return n * 2;
})
.findFirst();
System.out.println("First number > 3 (doubled): " + first.orElse(null));
/*
Filtering: 1
Filtering: 2
Filtering: 3
Filtering: 4
Mapping: 4
First number > 3 (doubled): 8
*/
Code language: PHP (php)
5. Pipeline with Aggregation
Group words by their length:
List<String> words = Arrays.asList("cat", "dog", "elephant", "rat", "rhinoceros");
Map<Integer, List<String>> wordsByLength = words.stream()
.filter(s -> s.length() > 2)
.collect(Collectors.groupingBy(String::length));
System.out.println("Words grouped by length: " + wordsByLength);
/*
Words grouped by length: {3=[cat, dog, rat], 8=[elephant], 10=[rhinoceros]}
*/
Code language: JavaScript (javascript)
Example: Complex Pipeline
Process a list of employees to find the names of high-salary employees in a specific department, sorted alphabetically:
import java.util.*; import java.util.stream.*; class Employee { String name; String department; double salary; Employee(String name, String department, double salary) { this.name = name; this.department = department; this.salary = salary; } public String getName() { return name; } public String getDepartment() { return department; } public double getSalary() { return salary; } } public class Main { public static void main(String[] args) { List<Employee> employees = Arrays.asList( new Employee("Aravind", "HR", 60000), new Employee("Bharat", "IT", 80000), new Employee("Charan", "HR", 75000), new Employee("Dharanish", "IT", 90000) ); List<String> highSalaryITNames = employees.stream() .filter(e -> e.getDepartment().equals("IT")) .filter(e -> e.getSalary() > 70000) .map(Employee::getName) .sorted() .collect(Collectors.toList()); System.out.println("High-salary IT employees: " + highSalaryITNames); } } /* High-salary IT employees: [Bharat, Dharanish] */