Java Stream Use Cases

Introduction

In this tutorial we will learn how to use streams in Java with various use cases.

Before that let us have a look at definition of stream.

As per stream official documention

A stream pipeline consists of a source (which might be an array, a collection, a generator function, an I/O channel, etc), zero or more intermediate operations (which transform a stream into another stream, such as filter(Predicate)), and a terminal operation (which produces a result or side-effect, such as count() or forEach(Consumer)).

Streams are lazy; computation on the source data is only performed when the terminal operation is initiated, and source elements are consumed only as needed.

Let us understand use cases one by one.

1. Filtering

If we want to filter elements from the given collection we can use filter() method on the stream.

filter() method takes Predicate as input to determine logic for filtering.

In the above example, given a list of numbers in collection we want only numbers greater then 40.

1
2
3
4
List<Integer> numbers = Arrays.asList(22,10,40,5,90,33,57);
numbers.stream()
       .filter(x -> x>40)
       .forEach(x-> System.out.println(x)); // 90 57

2. Modifying all elements in the source

All the elements in the source can be changed using map() function which takes in Function(Functional interface) as the argument.

In Function interface we can specify transformation logic.

In the below example we would be doubling each element in the list.

1
2
3
4
List<Integer> numbers = Arrays.asList(24,10,40,5,90,33,57);
numbers.stream()
       .map(x-> x*2)
       .forEach(x-> System.out.print(x+" ")); //48 20 80 10 180 66 114

3. Counting number of elements

We can count number of elements in the source using count() method.

count() method is terminal operation which gives no of elements in the source.

In the below example we just print number of elements in the list.

1
2
List<Integer> numbers = Arrays.asList(24,10,40,5,90,33,57);        
System.out.println(numbers.stream().count()); // 7

4. Finding maximum/maximum element in the collection

Given a list we can find maximum/minimum number in the list.

max() method takes in Comparator which decides sorting order.

In the below example we are finding maximum element in the list.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
List<Integer> numbers = Arrays.asList(24,10,40,5,90,33,57);
Optional<Integer> max = numbers
                       .stream()
                       .max(new Comparator<Integer>() {
                            @Override
                            public int compare(Integer o1, Integer o2) {
                            return o1 - o2;
                            }
                        });
System.out.println(max.get()); // 90

Using lambda expression we can write the same code.

1
2
List<Integer> numbers = Arrays.asList(24,10,40,5,90,33,57);
System.out.println(numbers.stream().max((o1,o2)-> o1 -o2).get());  //90

Using method reference we can write the same code.

1
2
List<Integer> numbers = Arrays.asList(24,10,40,5,90,33,57);
System.out.println(numbers.stream().max(Integer::compareTo).get()); // 90

Similarly for finding minimum value in the list, we can use min() method as shown in below code.

1
2
List<Integer> numbers = Arrays.asList(24,10,40,5,90,33,57);
System.out.println(numbers.stream().min(Integer::compareTo).get()); // 5

5. Finding distinct element in the collection

To get distinct element in the collection we can use distinct() method.

It will remove all duplicate elements in the list as shown below.

1
2
3
4
List<Integer> numbers = Arrays.asList(24,10,40,5,90,40,33,57,10);
numbers.stream()
       .distinct()
       .forEach(x -> System.out.print(x+" ")); //24 10 40 5 90 33 57

6. Sorting element in the collection

We can sort elements based on their natural order or custom order by specifying the comparator.

1
2
3
4
5
List<Integer> numbers = Arrays.asList(24,10,40,5,90,40,33,57,10);
numbers.stream()
        .distinct()
        .sorted()
        .forEach(x -> System.out.print(x+" ")); // 5 10 24 33 40 57 90

Similarly for strings we can sort as shown in below example.

1
2
3
4
List<String> names = Arrays.asList("Tim","Jim","Zara","Andy","Mark");
names.stream()
     .sorted()
     .forEach(x -> System.out.print(x+" ")); // Andy Jim Mark Tim Zara

To reverse the order of sorting you can specify Comparator.reverseOrder() as an argument to sorted() method.

1
2
3
4
List<String> names = Arrays.asList("Tim","Jim","Zara","Andy","Mark");
names.stream()
     .sorted(Comparator.reverseOrder())
     .forEach(x -> System.out.print(x+" "));

7. Reduction: Reducing collection to a single element

A reduction operation takes a sequence of input elements and combines them into a single summary result.

reduce operation is described in detail in my blog post Guide: Java Stream Reduce.

Let us take list of numbers and sum them up.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import java.util.Arrays;
import java.util.List;

public class ReduceDemo {

    public static void main(String[] args) {
        List<Integer> nums = Arrays.asList(10,20,30,40,50);

        int sum = nums.stream().reduce(0,(subtotal,nextElement) -> subtotal+nextElement);

        System.out.println(sum); // 150
    }
}

8. limit() operation

We can restrict number of elements processed by the stream using limit(maxSize) method.

We can mention no of elements to be returned by specifying the maximum size as an argument in limit() method.

1
2
3
4
5
6
7
List<Integer> numbers = Arrays.asList(24,10,40,5,90,40,33,57,10);
numbers.stream().limit(3).forEach(System.out::println);

Output:
24
10
40

9. findFirst() and findAny() operations

If we want to get first element processed by stream we can use findfirst() method which is guaranteed to return first element.

Even in parallel mode it will return the first element.

findAny() can return any element processed by the stream in parallel.

Any element can returned, nothing is guaranteed.

1
2
3
4
5
6
7
List<Integer> numbers = Arrays.asList(24,10,40,5,90,41,33,57,10);
System.out.println( numbers.parallelStream().findAny().get());
System.out.println( numbers.parallelStream().findFirst().get());

Output:
41 --> Can return any element
24 --> returns first element

10. anyMatch() and allMatch() operations

To check for specific condition based on values in the stream we can use match methods.

anyMatch(Predicate) will check if atleast element in the stream matches the condition and returns boolean.

allMatch(Predicate) will enforce strict condition like all the elements in the stream should match the condition and return boolean.

1
2
3
4
5
6
7
8
List<Integer> numbers = Arrays.asList(24,10,40,5,90,41,33,57,10);

System.out.println(numbers.stream().anyMatch(s-> s>50)); // true
System.out.println(numbers.stream().anyMatch(s-> s>100)); //false


System.out.println(numbers.stream().allMatch(s-> s>1)); //true
System.out.println(numbers.stream().allMatch(s-> s>50)); //false

11. Collecting elements in a different collection after processing

After processing elements we can choose to collect elements in a different collection using collect() method based on our requirement.

collect() takes implementations of Collector that implement various useful reduction operations such as accumulating elements into collections, summarizing elements according to various criteria, etc.

1
2
3
4
5
6
7
List<Integer> numbers = Arrays.asList(24,10,40,5,90,41,33,57,10);

List<Integer> result = numbers
        .stream()
        .filter(x -> x > 40).collect(Collectors.toList()); //[90, 41, 57]

System.out.println(result);
1
2
3
4
5
6
7
List<Integer> numbers = Arrays.asList(24,10,40,5,90,41,33,57,10);

Set<Integer> result = numbers
        .stream()
        .collect(Collectors.toSet()); //[33, 5, 24, 40, 41, 57, 10, 90]

System.out.println(result);