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 in Java offer powerful tools for data manipulation and transformation. Below are some advanced stream operations:
filter()
operationThe 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 wordsList<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 wordsSystem.out.println(filteredWords);}}
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.
flatMap()
operationThe 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 integersList<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 listList<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 listSystem.out.println(flattenedList);}}
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.
reduce()
operationThe 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 integersList<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);// Calculate the sum of the numbers using stream and reduceint sum = numbers.stream().reduce(0, Integer::sum);// Print the sumSystem.out.println(sum);}}
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.
collect()
operationThe 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 stringsList<String> strings = Arrays.asList("a", "b", "c", "d");// Use Java Streams to concatenate the strings with a delimiterString result = strings.stream().collect(Collectors.joining(", "));// Print the concatenated resultSystem.out.println(result);}}
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.
groupingBy()
operationThe 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 namesList<String> names = Arrays.asList("John", "Paul", "George", "Ringo");// Group names by their lengths using Java streams and Collectors.groupingByMap<Integer, List<String>> namesByLength = names.stream().collect(Collectors.groupingBy(String::length));// Print the map containing names grouped by lengthSystem.out.println(namesByLength);}}
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.
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