Intermediate vs Terminal Operations in Java 8
| Feature | Intermediate Operation | Terminal Operation |
|---|---|---|
| Definition | Returns a new Stream | Produces a result or side-effect, ends the stream |
| Behavior | Lazy – Doesn’t execute until terminal called | Eager – Triggers full execution of the stream |
| Chaining | Can be chained one after another | Must be the last operation |
| Return Type | Stream | Not a Stream (e.g., int, List, Optional) |
| Example | filter(), map(), sorted() |
forEach(), collect(), count() |
🔄 Intermediate Operations (with Examples)
These transform the stream, and can be chained.
| Operation | Description |
|---|---|
filter | Filter elements |
map | Transform each element |
flatMap | Flatten nested structure |
distinct | Remove duplicates |
sorted | Sort elements |
limit | Take only N elements |
skip | Skip first N elements |
peek | Look into elements (debug/logging) |
mapToInt | Convert to IntStream |
mapToLong | Convert to LongStream |
mapToDouble | Convert to DoubleStream |
takeWhile | Take elements while condition is true |
dropWhile | Drop elements while condition is true |
1. filter(Predicate)
Filters elements based on a condition.
List<Integer> list = List.of(1, 2, 3, 4, 5);
list.stream().filter(n -> n % 2 == 0).forEach(System.out::println); // 2, 4
exmaple:You are given a list of Employee objects, where each Employee has the following properties:
name (String), salary (double), department (String), isActive (boolean), joiningDate (LocalDate)
Write a stream pipeline to:
Filter only active employees,
Who belong to "IT" department,
Have a salary greater than ₹50,000,
And joined after Jan 1, 2022,
Then collect them into a List<Employee>.
code:-List<Employee> employeeList1 =Arrays.asList( new Employee("Alice", 50001,"IT",true, new Date()),
new Employee("Bob", 70000,"HR",true,new Date()),
new Employee("Charlie", 60000,"IT",true, new Date())
, new Employee("Bob", 75000,"IT",true,new Date()));
List<Employee> it = employeeList1.stream()
.filter(e -> e.isActive())
.filter(e -> e.getSalary() > 50000)
.filter(e -> e.getDepartment().equalsIgnoreCase("IT"))
.filter(e -> e.getJoiningDate().after(new Date(2025, 3, 1)))
.collect(Collectors.toList());
System.out.println("IT "+it);
2. map(Function)
Transforms each element (like convert String to upper-case).
List<String> names = List.of("java", "spring");
names.stream().map(String::toUpperCase).forEach(System.out::println); // JAVA, SPRING
List<Employee> employeeList =Arrays.asList( new Employee("Alice", 50000),
new Employee("Bob", 70000), new Employee("Charlie", 60000));
List<Employee> collect = employeeList.stream().sorted(Comparator.comparing(Employee::getSalary).reversed())
.collect(Collectors.toList());
System.out.println("collect "+collect);
new Employee("Bob", 70000), new Employee("Charlie", 60000));
List<Employee> collect = employeeList.stream().sorted(Comparator.comparing(Employee::getSalary).reversed())
.collect(Collectors.toList());
System.out.println("collect "+collect);
3. flatMap(Function)
Flattens nested structures into a single stream.
List<List<String>> list = List.of(List.of("a", "b"), List.of("c"));
list.stream().flatMap(Collection::stream).forEach(System.out::println); // a b c
4. distinct()
Removes duplicate elements.
Stream.of(1, 2, 2, 3, 3, 4).distinct().forEach(System.out::println); // 1 2 3 4
5. sorted()
Sorts elements in natural order.
Stream.of(3, 1, 4, 2).sorted().forEach(System.out::println); // 1 2 3 4
6. sorted(Comparator)
Sorts elements with a custom comparator.
Stream.of("banana", "apple").sorted(Comparator.reverseOrder()).forEach(System.out::println); // banana apple
7. limit(n)
Limits the stream to first n elements.
Stream.of(1, 2, 3, 4, 5).limit(3).forEach(System.out::println); // 1 2 3
8. skip(n)
Skips the first n elements.
Stream.of(1, 2, 3, 4, 5).skip(2).forEach(System.out::println); // 3 4 5
9. peek()
Used for debugging — looks at elements without modifying them.
Stream.of(1, 2, 3)
.peek(n -> System.out.println("Processing: " + n))
.map(n -> n * 2)
.forEach(System.out::println);
10 mapToInt(Function)
Converts a Stream<T> to an IntStream.
List<String> numbers = List.of("10", "20", "30");
int sum = numbers.stream()
.mapToInt(Integer::parseInt) // Convert to IntStream
.sum(); // IntStream has sum(), average(), etc.
System.out.println("Sum: " + sum); // Output: 60
11.mapToLong(Function)
Converts a Stream<T> to a LongStream.
List<String> list = List.of("10000000000", "20000000000");
long total = list.stream()
.mapToLong(Long::parseLong)
.sum();
System.out.println("Total: " + total);
12.mapToDouble(Function)
Converts a Stream<T> to a DoubleStream.
List<String> values = List.of("12.5", "7.5");
double average = values.stream()
.mapToDouble(Double::parseDouble)
.average()
.orElse(0.0);
System.out.println("Average: " + average); // Output: 10.0
13.takeWhile(Predicate)
Takes elements from the stream as long as the condition is true, then stops as soon as it fails.
👉 Only available in Java 9+
List<Integer> nums = List.of(1, 2, 3, 4, 1, 2);
List<Integer> nums = List.of(1, 2, 3, 4, 1, 2);
List<Integer> result = nums.stream()
.takeWhile(n -> n < 4)
.collect(Collectors.toList());
System.out.println(result); // Output: [1, 2, 3]
14.dropWhile(Predicate)
Skips elements from the stream as long as the condition is true, and starts including elements once it fails.
List<Integer> nums = List.of(1, 2, 3, 4, 1, 2);
List<Integer> result = nums.stream()
.dropWhile(n -> n < 4)
.collect(Collectors.toList());
System.out.println(result); // Output: [4, 1, 2]
Method
Use Case Example
mapToInt
Calculate sum/average/max of integers from a String list
mapToLong
Handle large values (like IDs or timestamps)
mapToDouble
Work with decimals (e.g., prices, measurements)
takeWhile
Process only initial matching data (e.g., before cutoff)
dropWhile
Ignore unwanted beginning data (e.g., headers, preamble)
look this also-
List<Employee> employees=Arrays.asList( new Employee("Alice", 50000),
new Employee("Bob", 70000),
new Employee("Charlie", 60000),
new Employee("David", 80000));
/*.sorted(Comparator.comparing(Employee::getSalary)).*/
Map<String, List<Employee>> collect1 = employees.stream().collect(Collectors.groupingBy(e -> {
double sal= e.getSalary();
if (sal< 60000) return "low";
else if (sal<= 70000) return "medium";
else return "High";
}));
System.out.println("collect1 "+collect1);
List<Employee> empMaxSalList = Arrays.asList(new Employee("Alice", 50000),
new Employee("Bob", 70000),
new Employee("Charlie", 60000));
double maxSal = empMaxSalList.stream().map( e->e.getSalary()).max(Double::compareTo).get();
System.out.println("max salary "+maxSal);//.thenComparingDouble(Employee::getSalary))
Map<Boolean, List<Employee>> collect2 = empMaxSalList.stream().collect(Collectors.partitioningBy(e -> {
double sal = e.salary;
if (sal > 60000) return true;
else return false;
}));
System.out.println("collect2 "+collect2);
Terminal Operations (with Examples)
These produce a result or effect, and end the stream.
| Terminal Operation | Description |
|---|---|
forEach() | Iterate over each element |
toArray() | Convert stream to array |
reduce() | Reduce stream to a single value |
collect() | Collect elements into List, Set, etc. |
min() / max() | Get smallest/largest element |
count() | Count elements in the stream |
anyMatch() | Check if any element matches condition |
allMatch() | Check if all elements match condition |
noneMatch() | Check if no elements match condition |
findFirst() | Get the first element (if present) |
findAny() | Get any element (non-deterministic) |
1. forEach(Consumer)
Performs an action for each element.
Stream.of("A", "B").forEach(System.out::println);
2. collect(Collector)
Collects into a collection (List, Set, Map).
List<String> list = Stream.of("a", "b").collect(Collectors.toList());
3. toArray()
Converts to an array.
String[] array = Stream.of("x", "y").toArray(String[]::new);
4. reduce()
Combines elements into a single result.
int sum = Stream.of(1, 2, 3).reduce(0, Integer::sum); // 6
5. count()
Returns the number of elements.
long count = Stream.of(1, 2, 3).count(); // 3
6. min() / max()
Finds min/max using comparator.
Optional<Integer> min = Stream.of(3, 5, 1).min(Integer::compareTo);
7. findFirst() / findAny()
Returns first or any element.
Optional<String> first = Stream.of("x", "y").findFirst();
8. anyMatch(), allMatch(), noneMatch()
Checks for matching conditions.
Stream.of(1, 2, 3).anyMatch(n -> n > 2); // true
Stream.of(1, 2, 3).allMatch(n -> n > 0); // true
Stream.of(1, 2, 3).noneMatch(n -> n < 0); // true
9.findFirst()
This method returns the first element of the stream wrapped in an Optional<T>.
Use it when order matters, like in sequential streams
Real-world analogy:
"You’re in a queue for tickets. findFirst() gives the first person in the queue."
Example:
10.findAny()
This method returns any one element from the stream (non-deterministically), especially useful with parallel streams for better performance.
✅ Use it when order doesn’t matter, like in parallel streams or performance-critical tasks.
Real-world analogy:
"You walk into a store and grab any one item from a shelf without caring about order.
Example:
Real World Example:
Suppose you're filtering users older than 18 and collecting names:
List<User> users = getUsers();
List<String> adultNames = users.stream()
.filter(u -> u.getAge() > 18)
.map(User::getName)
.sorted()
.collect(Collectors.toList());
This uses:
-
filter()(intermediate) -
map()(intermediate) -
sorted()(intermediate) -
collect()(terminal)
