LEPL1402 – Java Streams
2023-12-14
Why functional programming?
Functions + immutable data
Code that is easier to reason about (shorter, clarity…)
Functional interfaces
Improved uncoupling of code
Lambda expressions
Better handling of concurrency
Stream API
Why functional programming?
Functions + immutable data
Code that is easier to reason about (shorter, clarity…)
Functional interfaces
Improved uncoupling of code Last week
Lambda expressions
Better handling of concurrency
Stream API
Why functional programming?
Functions + immutable data
Code that is easier to reason about (shorter, clarity…)
Functional interfaces
Improved uncoupling of code Last week
Lambda expressions
Better handling of concurrency
Today
Stream API
Image credits: https://innovationm.co/concept-of-stream-api-java1-8/
A stream is sequence of
elements of the same type
accessed one after the other
Image credits: https://innovationm.co/concept-of-stream-api-java1-8/
Stream pipelines provide a
A stream is sequence of declarative way to
elements of the same type perform computations
accessed one after the other on sequences of objects
Image credits: https://innovationm.co/concept-of-stream-api-java1-8/
Stream pipelines provide a
A stream is sequence of declarative way to
elements of the same type perform computations
accessed one after the other on sequences of objects
Streams are chained until
the terminal operation that
collects the results of the
Image credits: https://innovationm.co/concept-of-stream-api-java1-8/ stream pipeline
From miles to kilometers using streams
[ "15", "", "", "3.5", "" ] [ "24.14016", "5.6327043" ]
List<String> containing miles List<String> containing kilometers
(1 mile equals 1609.344 meters)
From miles to kilometers using streams
[ "15", "", "", "3.5", "" ] [ "24.14016", "5.6327043" ]
List<String> containing miles List<String> containing kilometers
(1 mile equals 1609.344 meters)
From miles to kilometers using streams
[ "15", "", "", "3.5", "" ] [ "24.14016", "5.6327043" ]
List<String> containing miles List<String> containing kilometers
(1 mile equals 1609.344 meters)
Streams can be
constructed out of
Java collections
From miles to kilometers using streams
[ "15", "", "", "3.5", "" ] [ "24.14016", "5.6327043" ]
List<String> containing miles List<String> containing kilometers
(1 mile equals 1609.344 meters)
Streams can be
constructed out of
Java collections
Lambda expressions
are used to declare
the stream pipeline
From miles to kilometers using streams
[ "15", "", "", "3.5", "" ] [ "24.14016", "5.6327043" ]
List<String> containing miles List<String> containing kilometers
(1 mile equals 1609.344 meters)
Streams can be
constructed out of
Java collections
Lambda expressions
are used to declare
the stream pipeline
Java collections can
be created out of
streams
The Stream interface
This is the main interface for streams
Elements of a stream
have a data type T
The Stream interface
Streams can be made explicit in our example:
This is the main interface for streams
Elements of a stream
have a data type T
Anatomy of a stream pipeline
Anatomy of a stream pipeline
Source methods (create a stream)
Anatomy of a stream pipeline
Source methods (create a stream)
Intermediate methods
(transform successive streams)
Anatomy of a stream pipeline
Source methods (create a stream)
Intermediate methods
(transform successive streams)
Terminal methods (consume a stream)
Source methods
Case 1: From a Java collection
Streams are frequently created out of one of the standard Java collections
(such as List or Set)
Case 2: Manual creation of a stream
Case 2: Manual creation of a stream
The compiler automatically
infers the type of the arguments
of Stream.of() from the type
of the target stream
This is also applicable to complex objects
Case 3: From an array
This is a static method from the
java.util.Arrays class
Case 4: From a reader
The lines are read one by
one, only when needed
(streams are lazy)
Case 5: Declaring an infinite stream
Content of the stream: seed
f(seed)
f(f(seed))
f(f(f(seed)))
…
Case 5: Declaring an infinite stream
Content of the stream: seed
f(seed)
f(f(seed))
f(f(f(seed))) This is OK because
streams are lazy,
… there is no infinity here!
Case 5: Declaring an infinite stream
Content of the stream: seed Generating the even numbers:
f(seed)
f(f(seed))
f(f(f(seed))) This is OK because
streams are lazy,
… there is no infinity here!
Intermediate methods
Map
Map
Apply a general-purpose unary
function to each element in the
stream, generally expressed as a
lambda expression
Map
The type of the elements in Apply a general-purpose unary
the output stream can change function to each element in the
stream, generally expressed as a
lambda expression
Examples of map
Incrementing numbers in a stream:
Examples of map
Incrementing numbers in a stream:
Examples of map
Incrementing numbers in a stream:
Computing the length of strings in a stream:
Examples of map
Incrementing numbers in a stream:
Computing the length of strings in a stream:
Examples of map
Incrementing numbers in a stream:
Computing the length of strings in a stream:
Extracting a member from a stream of complex objects:
Examples of map
Incrementing numbers in a stream:
Computing the length of strings in a stream:
Extracting a member from a stream of complex objects:
Examples of map
Incrementing numbers in a stream:
Computing the length of strings in a stream:
Extracting a member from a stream of complex objects:
Creating a stream of complex objects:
Examples of map
Incrementing numbers in a stream:
Computing the length of strings in a stream:
Extracting a member from a stream of complex objects:
Creating a stream of complex objects:
Filter
Filter
Keep the elements from a stream
that verify a general-purpose unary
predicate, generally expressed as
a lambda expression
Filter
Keep the elements from a stream
that verify a general-purpose unary
predicate, generally expressed as
a lambda expression
To keep only the integer multiples of 4:
Sorting streams
Sorting streams
Delegation to a
Comparator<T> is possible if
natural order is not suitable
Skipping or limiting streams
Skipping or limiting streams
The limit() operation
enables dealing with
infinite streams!
Terminal methods
Case 1: Consumers (yield no output)
This method applies the
consumer function to all the
elements of the stream
Case 1: Consumers (yield no output)
This method applies the
consumer function to all the
elements of the stream
Typical use case = printing the elements of a stream
Case 2: Collectors to create Java collections
The java.util.stream.Collectors
class contains predefined collectors that
are sufficient for most use cases
Case 3: Reduction methods
Reduction methods extract one single value out of a stream
(count, sum, average, minimum,...)
Case 3: Reduction methods
Reduction methods extract one single value out of a stream
(count, sum, average, minimum,...)
Case 3: Reduction methods
Reduction methods extract one single value out of a stream
(count, sum, average, minimum,...)
Successively apply the binary
operator accumulator to the
initial value identity, and to
the elements of the stream
Example: Reduction computing the sum
The initial value of the accumulator is zero,
which is the identity value for addition
●
Accumulator = 0
●
2 is read → accumulator becomes “0 + 2” = 2
●
5 is read → accumulator becomes “2 + 5” = 7
●
8 is read → accumulator becomes “7 + 8” = 15
●
The stream is over → 15 is returned
About laziness
About laziness
You could think that the code does the following:
●
A stream with elements 1, 2, 3, 4, and 5 is created.
● The lambda expression i -> { System.out.println(i); return i + 1; }
is applied to each element.
●
The console display the lines 1, 2, 3, 4, and 5.
●
A new stream containing 2, 3, 4, 5, and 6 is returned.
About laziness
You could think that the code does the following:
●
A stream with elements 1, 2, 3, 4, and 5 is created.
● The lambda expression i -> { System.out.println(i); return i + 1; }
is applied to each element.
●
The console display the lines 1, 2, 3, 4, and 5.
●
A new stream containing 2, 3, 4, 5, and 6 is returned.
About laziness
You could think that the code does the following:
●
A stream with elements 1, 2, 3, 4, and 5 is created.
● The lambda expression i -> { System.out.println(i); return i + 1; }
is applied to each element.
●
The console display the lines 1, 2, 3, 4, and 5.
●
A new stream containing 2, 3, 4, 5, and 6 is returned.
Streams are lazy. Operations are
only executed if the result is needed.
About interleaving
Output:
a: 1
b: 2
a: 2
b: 3
a: 3
b: 4
About interleaving
Output: To read one element of s2,
a: 1 one element of s1 must be read
b: 2
a: 2
b: 3
a: 3
b: 4
Immutable list
Side effects
Side effects
Side effects
A good function should have no
side effect. A function should
never change existing objects
and variables.
Wrapper types
Primitive types are mutable (int, float, double,...)
Wrapper types
Primitive types are mutable (int, float, double,...)
Their wrapper classes are immutable by design (Integer, Float, Double,...)
Wrapper types
Primitive types are mutable (int, float, double,...)
Their wrapper classes are immutable by design (Integer, Float, Double,...)
Could we define a list that is immutable?
Architecture
Architecture
Implementation
This data structure is
immutable. It cannot be
changed after creation.
Construction of an immutable list
Implementing forEach() to execute a consumer
This is a recursion!
Evaluation
Please help us improve this brand new course!
https://forms.office.com/Pages/ResponsePage.aspx?id=1JCwei76z068fEEntNWC7J5qKCyxXcROjhQa15tvWb9URURTSlZYVVRXRldJS1JIQlpJVDZES1ZMQy4u