Java advance stream operations

In modern Java programming, the Stream API introduced in Java 8 changed how developers work with collections of data. Streams make it easier to do complex tasks with data using a functional style. Below, we’ll look at some advanced stream operations in Java and show how to use them with examples.

Before we get into advanced stream operations, let’s quickly review what streams are and how they work. In Java, a stream is like a sequence of data from a source that we can work with in different ways. Streams help us perform complex tasks with data more easily and clearly.

Advanced stream operations

Advanced stream operations in Java offer powerful tools for data manipulation and transformation. Below are some advanced stream operations:

The filter() operation

The filter() operation removes elements from the stream that don’t match a given predicate.

import java.util.*;
import java.util.stream.*;
class Main {
public static void main(String[] args) {
// Create a list of words
List<String> words = Arrays.asList("hello", "world");
// Use Java streams to filter words starting with 'h'
List<String> filteredWords = words.stream()
.filter(s -> s.startsWith("h")) // Filter words starting with 'h'
.collect(Collectors.toList()); // Collect filtered words into a new list
// Print the filtered words
System.out.println(filteredWords);
}
}

Code explanation

  • Line 7: This creates a list named words that contains two strings: "hello" and "world". The Arrays.asList method is used to create a fixed-size list backed by an array.

  • Lines 10–12: This converts the list of words into a stream and then applies a filter operation using the filter method. The filter operation checks each string (s) in the stream and only keeps those strings that start with the letter "h". The Collectors.toList() method collects the filtered elements into a new list.

  • Line 15: This prints the filtered words.

The flatMap() operation

The flatMap() operation is used to flatten streams of collections into a single stream. It takes a function as input, which maps each element to a stream of values. The resulting streams are then concatenated into a single stream.

import java.util.*;
import java.util.stream.*;
class Main {
public static void main(String[] args) {
// Define a nested list containing lists of integers
List<List<Integer>> nestedList = Arrays.asList(
Arrays.asList(1, 2, 3),
Arrays.asList(4, 5, 6),
Arrays.asList(7, 8, 9)
);
// Use streams to flatten the nested list
List<Integer> flattenedList = nestedList.stream()
// Use flatMap to merge inner lists into a single stream
.flatMap(Collection::stream)
// Collect the elements into a new list
.collect(Collectors.toList());
// Print the flattened list
System.out.println(flattenedList);
}
}

Code explanation

  • Lines 7–11: This creates a nested list of integers using the Arrays.asList method. Each inner list contains integers, and the outer list contains these inner lists, resulting in a nested structure.

  • Lines 14–18: This converts the list into a stream of lists. flatMap(Collection::stream) is applied to each element of the stream. This flattens each inner list into a single stream of integers. flatMap is used here because each inner list is itself a collection, and we want to flatten all the inner lists into one single stream of integers. collect(Collectors.toList()) collects all the integers from the flattened stream into a new list.

  • Line 21: This prints the flattened list. The flattened list contains all the integers from the nested list in a single line.

The reduce() operation

The reduce() operation combines the elements of a stream into a single result. It takes an initial value (also called identity) and a BinaryOperator as parameters to perform the reduction.

import java.util.*;
import java.util.stream.*;
class Main {
public static void main(String[] args) {
// Create a list of integers
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// Calculate the sum of the numbers using stream and reduce
int sum = numbers.stream().reduce(0, Integer::sum);
// Print the sum
System.out.println(sum);
}
}

Code explanation

  • Line 7: This creates a list of integers named numbers using the Arrays.asList method.

  • Line 10: The list is transformed into a stream of integers, and then a reduction operation using reduce(0, Integer::sum) is performed on the stream. Here, the initial value of 0 serves as the starting point for the summation process. The Integer::sum method reference points to the sum method within the Integer class, which is employed to accumulate the elements within the stream.

  • Line 13: This prints the value of the sum variable.

The collect() operation

The collect() operation accumulates elements of a stream into a mutable result container, such as a list, set, or map.

import java.util.*;
import java.util.stream.*;
class Main {
public static void main(String[] args) {
// Create a list of strings
List<String> strings = Arrays.asList("a", "b", "c", "d");
// Use Java Streams to concatenate the strings with a delimiter
String result = strings.stream()
.collect(Collectors.joining(", "));
// Print the concatenated result
System.out.println(result);
}
}

Code explanation

  • Line 7: This creates a list of strings using the Arrays.asList method.

  • Lines 10–11: This converts the list into a stream of strings. collect(Collectors.joining(", ")) collects the elements of the stream into a single string, where , is used as the delimiter between consecutive elements. The joining collector joins the elements using the specified delimiter.

  • Line 14: This prints the concatenated string.

The groupingBy() operation

The groupingBy() operation is used to group elements of a stream by a classifier function, resulting in a Map where keys are the result of the classifier function, and values are lists of elements.

import java.util.*;
import java.util.stream.*;
class Main {
public static void main(String[] args) {
// Create a list of names
List<String> names = Arrays.asList("John", "Paul", "George", "Ringo");
// Group names by their lengths using Java streams and Collectors.groupingBy
Map<Integer, List<String>> namesByLength = names.stream()
.collect(Collectors.groupingBy(String::length));
// Print the map containing names grouped by length
System.out.println(namesByLength);
}
}

Code explanation

  • Line 7: This creates a list of names using the Arrays.asList method.

  • Lines 10–11: This converts the list into a stream of strings. collect(Collectors.groupingBy(String::length)) collects the elements of the stream into a map, where the keys are the lengths of the strings and the values are lists of strings with the same length. The groupingBy collector groups elements of the stream by a classifier function, which in this case is String::length. This function maps each string to its length, and elements with the same length are grouped together.

  • Line 14: This prints the map namesByLength containing the names grouped by their lengths.

Conclusion

The Stream API in Java provides a powerful set of tools for processing collections of data in a functional style. By mastering advanced stream operations, developers can write more concise, readable, and efficient code for data manipulation tasks. Understanding these operations is essential for leveraging the full potential of the Stream API in Java programming.

We’ve only scratched the surface of what streams can do. As we continue our journey with Java development, exploring and mastering the intricacies of stream operations will undoubtedly enhance our programming skills and productivity.

Free Resources

Copyright ©2025 Educative, Inc. All rights reserved