0% found this document useful (0 votes)
13 views29 pages

Streams: Concepts

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
13 views29 pages

Streams: Concepts

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 29

Streams

Concepts
Stream

Working with Streams

Intermediate Operations

filter()

map()

sorted()

distinct()

Terminal Operations

forEach()

count()

collect()

anyMatch()

allMatch()

noneMatch()

Introduction

In the course, we have learned different data structures likes, arrays, sets, etc. To
perform some operations on these data, we will iterate over the data and perform
operations on the original data. Java provides a way for processing data in a safer
and concise way through Streams.

In this unit, we'll learn about Streams in Java.

1. Stream

A stream is a sequence of elements that supports various operations for processing


the elements.

It's important to note that a stream does not store the elements of an array, etc, it
simply provides a way to access them.

It means that we can perform operations on a stream without modifying the original
data.

Java consists of a

Stream interface from the java.util.stream package consists of all the methods
used to perform operations on streams.

It does not support primitive types such as

int , long , and double . To use the Stream interface with primitive types,
we must use the wrapper classes Integer , Long , and Double , respectively.

1.1 Creating an Empty Stream

The

Stream interface consists of a static and default method empty() that can
be used to create an empty stream.

Syntax
JAVA

1 Stream.empty();

Here,

Stream is the interface.


Code
JAVA

1 import java.util.stream.Stream;
2
3 class Main {
4 public static void main(String[] arg
5 Stream<String> stream = Stream.e
6 }
7 }

In the above code, we have created an empty stream using the default method

empty() . As the method empty() is a static method, we are able to invoke it


directly using the interface as Stream.empty() .

1.2 Creating a Stream with Collections

All the classes that implement

Collection interface (for example, ArrayList, HashSet) consists of a stream()


method that can be used for creating a stream of its corresponding type.

Syntax
JAVA

1 collection.stream()

The

stream() methods returns a Stream of elements in the collection.

Code
JAVA

1 import java.util.*;
2 import java.util.stream.Stream;
3
4 class Main {
5 public static void main(String[] ar
6 List<Integer> numbers = Arrays
7
7
8 Stream<Integer> numStream = num
9 }
10 }

In the above code, we have created a list of

Integer s and assigned it to numbers variable. We can notice that the type of
numbers is mentioned as List . The interfaces cannot be instantiated but can be
used for referencing. Hence, the above code executes successfully.

From the list of

numbers we have created a stream and assigned it to the numStream . Here,


the stream does not occupy any memory, instead it just provides a way to access the
original elements.

We can also create a stream directly using the

of() method of the Stream interface.

Syntax
JAVA

1 Stream.of(list of elements);

Code
JAVA

1 import java.util.stream.Stream;
2
3 class Main {
4 public static void main(String[] arg
5 Stream<Integer> stream = Stream
6 }
7 }
2. Working with Streams

Working with Streams include a stream source followed by zero or more


intermediate operations and a terminal operation.

1. Stream source: A stream source is an instance of the stream that consists of


elements.
2. Intermediate operation: An intermediate operation is a stream operation that
produces a new stream as output after performing the specified operations.
3. Terminal operation: A terminal operation is a stream operation that consumes
the elements of a stream and produces a result.

Code
JAVA

1 import java.util.*;
2 import java.util.stream.Stream;
3
4 class Main {
5 public static void main(String[]
6 List<Integer> numbers = Array
7
8 Stream<Integer> numStream =
9 Stream<Integer> filteredStre
10 filteredStream.forEach(n -> S
11 }
Expand

Output

2
4

The above code creates a stream of the list provided, filters the even numbers, and
displays each even number in a new line on the console.
We'll learn more about
filter() and forEach() methods later in this unit.

On every stream we can perform only one operation. After every operation the
stream is consumed/closed.

Let's try to perform an operation twice on a single stream

Code
JAVA

1 import java.util.*;
2 import java.util.stream.Stream;
3
4 class Main {
5 public static void main(String[]
6 List<Integer> numbers = Array
7
8 Stream<Integer> numStream =
9 Stream<Integer> filteredStre
10 Stream<Integer> filteredStre
11 }
Expand

Output

Exception in thread "main" java.lang.Il

In the above code, we tried to perform operations twice on a single stream

numStream . As after every operation the stream is consumed/closed, an


exception has been thrown.

2.1 Chaining Streams

We can combine multiple stream operations together to form a single expression.


JAVA

1 streamSource.intermediateOperation().ter

Code
JAVA

1 import java.util.*;
2
3 class Main {
4 public static void main(String[]
5 List<String> names = Arrays.
6
7 names.stream() // stream so
8 .filter(eachName -> eac
9 .forEach(name -> System
10 }
11 }
Expand

Output

Alice
Charlie

In the above code, we have created a stream of strings, filtered the names that have
more than 4 letters and displayed the resultant names on the console. We have
written all these operations in a single statement.

3. Intermediate Operations

An intermediate operation is a stream operation that produces a new stream as


output after performing the specified operations. A stream can have zero or multiple
intermediate operation.

let's learn some of the commonly used methods to perform intermediate operations
on streams:

1. filter()
2. map()
3. sorted()
4. distinct()

1. filter()

The

filter() method is used to filter the elements of a stream based on a condition.

It accepts a

Predicate (a functional interface) type as an argument and returns a stream that


contains filtered elements based on the specified condition.

Syntax
JAVA

1 stream.filter(Predicate)

Code
JAVA

1 import java.util.*;
2 import java.util.function.Predicate;
3
4 class Main {
5 public static void main(String[]
6 List<String> names = Arrays.
7
8 Predicate<String> predi = (e
9
10 names.stream() // stream so
11 .filter(predi) // inte
Expand
Output

Alice
Charlie

In the above code, we have created a stream of strings, filtered the names that have
more than 4 letters and displayed the resultant names on the console.

We can also write the above code as,

Code
JAVA

1 import java.util.*;
2
3 class Main {
4 public static void main(String[]
5 List<String> names = Arrays.
6
7 names.stream() // stream so
8 .filter(eachName -> eac
9 .forEach(name -> System
10 }
11 }
Expand

Output

Alice
Charlie

In the above code, the Java compiler recognizes that the lambda expression

eachName -> eachName.length() > 4 is being used to implement the Predicate


interface.

2. map()
The

map() method is used to perform an operation on each element of the stream.

It accepts a

Function (a functional interface) type as an argument and returns a stream of


elements after performing the specified operation. For example, multiplying each
element of the stream by 2.

Syntax
JAVA

1 stream.map(Function)

Code
JAVA

1 import java.util.*;
2
3 class Main {
4 public static void main(String[]
5 List<Integer> numbers = Array
6
7 numbers.stream() // stream
8 .map(eachNumber -> ea
9 .forEach(number -> Sy
10 }
11 }
Expand

Output

4
10
12
2

In the above code, the Java compiler recognizes that the lambda expression
eachNumber -> eachNumber * 2 is being used to implement the Function
interface.

3. sorted()

The

sorted() method is used to sort the elements of the stream in ascending order.

Syntax
JAVA

1 stream.sorted()

Example 1: Sorting numbers

Code
JAVA

1 import java.util.*;
2
3 class Main {
4 public static void main(String[]
5 List<Integer> numbers = Array
6
7 numbers.stream() // stream
8 .sorted() // intermed
9 .forEach(number -> Sy
10 }
11 }
Expand

Output

1
2
5
6
In the above code, the

sorted() method has arranged the elements of the numbers stream in ascending
order and returned a new stream. On the new stream, we have performed a terminal
operation using the forEach() method and displayed the sorted numbers on the
console.

Example 2: Sorting strings

Code
JAVA

1 import java.util.*;
2
3 class Main {
4 public static void main(String[]
5 List<String> names = Arrays.
6
7 names.stream() // stream so
8 .sorted() // intermedi
9 .forEach(name -> System
10 }
11 }
Expand

Output

Alice
Dave
bob
charlie

In the above code, the

sorted() method has arranged the elements of the numbers stream in ascending
order based on the Unicode values of the characters in the string and returned a new
stream. On the new stream, we have performed a terminal operation using the
forEach() method and displayed the sorted strings on the console.

3.1 sorted(Comparator)

The
sorted() method also accepts Comparator (a functional interface) type as an
argument and returns a stream of the sorted elements based on the specified
operation.

Syntax
JAVA

1 stream.sorted(Comparator)

Example 1:

Code
JAVA

1 import java.util.*;
2
3 class Main {
4 public static void main(String[]
5 List<Integer> numbers = Array
6
7 numbers.stream()
8 .sorted((num1, num2)
9 .forEach(number -> Sy
10 }
11 }
Expand

Output

6
5
2
1

In the above code, the Java compiler recognizes that the lambda expression

(num1, num2) -> num2 - num1 is being used to implement the Comparator
interface.

The lambda expression used in the above code calculates the difference
num2 - num1 which results in a positive number if num2 > num1 , a
negative number if num1 > num2 , and zero if num2 == num1 . The
sorted() method internally sorts the numbers based on these results and returns a
stream containing the numbers in descending order.

We can also use the

reverseOrder() method of the Comparator interface to sort the elements in


descending order.

Example 2:

Code
JAVA

1 import java.util.*;
2
3 class Main {
4 public static void main(String[]
5 List<Integer> numbers = Array
6
7 numbers.stream()
8 .sorted(Comparator.rev
9 .forEach(number -> Sy
10 }
11 }
Expand

Output

6
5
2
1

Example 3:

Code
JAVA

1 import java.util.*;
2
3 class Main {
4 public static void main(String[]
5 List<String> names = Arrays.
6
7 names.stream()
8 .sorted(Comparator.reve
9 .forEach(name -> System
10 }
11 }
Expand

Output

charlie
bob
Dave
Alice

3.2 Sorting Custom Objects

Let's create a custom class

Person with a few attributes, instantiate multiple objects of the class, and then
sort these objects in ascending order based on a specific attribute.

Code
JAVA

1 import java.util.*;
2
3 class Person {
4 String name;
5 int age;
6
7 public Person(String name, int ag
8 this.name = name;
9 this.age = age;
10 }
11 }
Expand

Output
Alice
Bob
Charlie

In this code, we create a

Person class with name and age attributes and a constructor. In the
main method of the Main class, we create an ArrayList of Person objects
and add three Person instances with different names and ages.

We then use Java streams to sort the

persons ArrayList based on the name attribute in ascending order. Finally, we


use the forEach() method to print the sorted names of the Person objects.

4. distinct()

The

distinct() method is used to remove duplicate elements from the stream.

Syntax
JAVA

1 stream.distinct()

Example 1:

Code
JAVA

1 import java.util.*;
2
3 class Main {
4 public static void main(String[]
5 List<String> names = Arrays.
6
7 names.stream()
8 .distinct()
9 .forEach(name -> System
10 }
11 }
Expand

Output

bob
Alice

In the above code, the

distinct() method has removed the duplicate strings from the stream and returned
a new stream. On the new stream, we have performed a terminal operation using the
forEach() method and displayed the unique strings on the console.

Example 2:

Code
JAVA

1 import java.util.*;
2
3 class Main {
4 public static void main(String[]
5 List<Integer> numbers = Array
6
7 numbers.stream()
8 .distinct()
9 .forEach(number -> Sy
10 }
11 }
Expand

Output

2
5

In the above code, the


distinct() method has removed the duplicate numbers from the stream and
returned a new stream. On the new stream, we have performed a terminal operation
using the forEach() method and displayed the unique numbers on the console.

4. Terminal Operations

A terminal operation is a stream operation that consumes the elements of a stream


and produces a result. We cannot perform any intermediate or another terminal
operation after a terminal operation has been performed on a stream.

let's learn some of the commonly used methods to perform terminal operations on
streams:

1. forEach()
2. count()
3. collect()
4. anyMatch()
5. allMatch()
6. noneMatch()

1. forEach()

The

forEach() method works similar to the map() . It iterates over the elements in
the stream and performs the specified operation on each element. Unlike map() ,
the forEach() method does not return any stream.

It accepts a

Consumer (a functional interface) type as an argument and performs the specified


operation on each element.

Syntax
JAVA

1 stream.forEach(Consumer);

Code
JAVA

1 import java.util.*;
2
3 class Main {
4 public static void main(String[]
5 List<String> names = Arrays.
6
7 names.stream() // stream so
8 .filter((eachName) -> e
9 .forEach(name -> System
10 }
11 }
Expand

Output

Alice
Charlie

In the above code, we have used the

forEach() method to display the elements of the resultant stream on the console.

2. count()

The

count() method returns a long integer that indicates the number of elements
present in the stream.

Syntax
JAVA

1 stream.count();

Code
JAVA

1 import java.util.*;
2
3 class Main {
4 public static void main(String[]
5 List<String> names = Arrays.
6
7 long namesCount = names.stre
8 .filte
9 .count
10
11 System.out.println(namesCount
Expand

Output

In the above code, we have filtered the names that has greater than 4 letters and used
the

count() method to get the number of names in the resultant stream.

3. collect()

The

collect() is commonly used to perform a reduction operation on the elements of a


stream and produce a result. For example, the operations may involve calculating the
sum of a list of numbers, finding the minimum or maximum element in a stream,
grouping elements by some criterion, etc.

Reduction Operation: Reduction operations are operations that reduce a


stream of elements to a single value.
The

collect() method accepts a Collector (interface) type and returns the result
after performing the specified operation.

Syntax
JAVA

1 stream.collect(Collector);

There is a

Collectors class in java.util.stream package that provides implementations for


many common operations. For example, calculating the sum of a list of numbers etc.

3.1 Performing a Reduction Operation using

collect() Method

Let's learn how to calculate the sum of double of numbers in a stream using the

collect() method.

The

Collectors class implements summingInt() method which accepts


ToIntFunction (functional interface) type as an argument and provides the sum of
elements after performing the specified operation.

Code
JAVA

1 import java.util.*;
2 import java.util.stream.Collectors;
3
4 class Main {
5 public static void main(String[]
6 List<Integer> numbers = Array
7
8 int sumOfNums = numbers.stre
9 .colle
10
11 System.out.println(sumOfNums
Expand
Output

28

In the above code, we have used the

collect() method to determine sum of double of numbers in a stream. We have


provided Collectors.summingInt() method as an argument. To the
summingInt() method, we have provided a lambda expression which double the
element.

Hence, we got the result of the sum of all the elements in the list multiplied by 2.

3.2 Converting a Stream to a Collection

We can use the below methods to convert a stream to a

Collection

Collectors.toList() -> converts the stream of elements to a List .


Collectors.toSet() -> converts the stream of elements to a Set .
Collectors.toMap() -> converts the stream of elements to a Map .

Example 1: Converting to a

List

Code
JAVA

1 import java.util.*;
2 import java.util.stream.Collectors;
3
4 class Main {
5 public static void main(String[]
6 List<Integer> numbers = Array
7
8 List<Integer> filteredNums =
9 .filter(
10 .collect
11
Expand
Output

[5, 6, 5]

In the above code, we have created a stream, filtered the numbers that are greater
than 2, and converted the stream of the resultant elements to a

List .

Example 2: Converting to a

Set

Code
JAVA

1 import java.util.*;
2 import java.util.stream.Collectors;
3
4 class Main {
5 public static void main(String[]
6 List<Integer> numbers = Array
7
8 Set<Integer> set = numbers.st
9 .filter(num
10 .collect(Col
11
Expand

Output

[5, 6]

In the above code, we have created a stream, filtered the numbers that are greater
than 2, and converted the stream of the resultant elements to a

Set .
As the

Set contains only unique elements it returned only [5, 6] as the unique
elements after filtering.

Example 3: Converting to a

Map

The

Collectors.toMap() accepts two Function s (functional interface) type as


arguments. The first argument maps the keys and the second argument is used for
value mapping.

Code
JAVA

1 import java.util.*;
2 import java.util.stream.Collectors;
3
4 class Main {
5 public static void main(String[]
6 List<String> words = Arrays.
7
8 Map<String, Integer> wordLeng
9 .collect(Collectors.t
10 word -> word
11 word -> word
Expand

Output

{Bob=3, Alice=5, Charlie=7, Dave=4}

In the above code, we have provided the lambda expression

word -> word as the first argument to the toMap() method which maps the
keys. As the second argument, the lambda expression word -> word.length() has
been provided that calculates the word length and maps the value to its
corresponding key.

Other useful methods of the


Collectors class,

Method Description

used to calculate the average of elements of a


averagingInt()
stream
used to group the elements of a stream by a
groupingBy()
certain criterion
used to divide the elements of a stream into two
partitioningBy()
groups based on a condition
used to concatenate the elements of a stream into
joining()
a single string.

4. anyMatch()

The

anyMatch() method is used to check if any of elements in the stream has matched
a condition. It returns a boolean data type.

It accepts a

Predicate (a functional interface) type as an argument.

Syntax
JAVA

1 stream.anyMatch(Predicate);

Code
JAVA

1 import java.util.*;
2
3 class Main {
4 public static void main(String[]
5 List<String> names = Arrays.
6
7 boolean isNamePresent = name
8 .anyMatch(eachName -
9
10 System.out.println(isNamePre
11 }
Expand
Output

true

In the above code, we have created a stream of strings and checked if any of the
given names is greater than 4 in length. In the names provided, one of the names
Alice is greater than 4 in length. So, the true text is displayed on the console.

5. allMatch()

The

allMatch() method is used to check if all the elements in the stream match a
condition. It returns a boolean data type.

It accepts a

Predicate (a functional interface) type as an argument.

Syntax
JAVA

1 stream.allMatch(Predicate);

Code
JAVA

1 import java.util.*;
2
3 class Main {
4 public static void main(String[]
5 List<String> names = Arrays.
6
7 boolean isNamePresent = name
8 .allMatch(eachName -
9
10 System.out.println(isNamePre
11 }
Expand

Output

false

In the above code, we have created a stream of strings and checked if all the given
names are greater than 4 in length. In the names provided, one of the names Bob is
not greater than 4 in length. So, the false text is displayed on the console.

6. noneMatch()

The

noneMatch() method is used to check if no element in the stream matches a


condition. It returns a boolean data type.

It accepts a

Predicate (a functional interface) type as an argument.

Syntax
JAVA

1 stream.noneMatch(Predicate);
Code
JAVA

1 import java.util.*;
2
3 class Main {
4 public static void main(String[]
5 List<String> names = Arrays.
6
7 boolean isNamePresent = name
8 .noneMatch(eachName
9
10 System.out.println(isNamePre
11 }
Expand

Output

true

In the above code, we have created a stream of strings and checked that none of the
given names is less than 2 in length. The true text is displayed on the console as
none of the given names is less than 2 in length.

Summary
Stream
A stream does not store the elements, it simply provides a way to access
them.

On every stream, we can perform only one operation. After every


operation the stream is consumed/closed.
Stream source: A stream source is an instance of the stream that consists
of elements.

Intermediate operation: An intermediate operation is a stream


operation that produces a new stream as output after performing the
specified operation.

Terminal operation: A terminal operation is a stream operation that


consumes the elements of a stream and produces a result.
Intermediate operations
filter() : used to filter the elements of a stream based on a condition.

map() : used to perform an operation on each element of the stream.

sorted() : used to sort the elements of the stream.

distinct() : used to remove duplicate elements from the stream.

Terminal operations
forEach() : used to iterate and perform the specified operation on each
element of the stream.

count() : used to count the number of elements in a stream.

collect() : commonly used to perform a reduction operation on the


elements of a stream and produce a result.

anyMatch() : used to check if any of elements in the stream has


matched a specified condition.

allMatch() : used to check if all the elements in the stream match a


specified condition.

noneMatch() : used to check if no element in the stream matches a


specified condition.

You might also like