Java
Stream
API
softserve
Agenda
•Class [Link]
•Stream API. Aggregate Operations
•Case Studies
Class
[Link]
Pipeline
Class Stream
• Stream is a new abstract layer introduced in Java 8.
• Using stream, you can process data in a declarative way.
• Using collections framework in Java, a developer has to use loops and make repeated
checks.
• Another concern is efficiency; as multi-core processors are available at ease; a Java
developer has to write parallel code processing that can be pretty error-prone.
• To resolve such issues, Java 8 introduced the concept of stream that lets the developer to
process data declaratively and leverage multicore architecture without the need to write any
specific code for it.
Class Stream
• Stream represents a sequence of objects from a source, which supports aggregate
operations.
• Characteristics of a Stream
• Sequence of elements. A stream provides a set of elements of specific type in a
sequential manner.
• A stream gets/computes elements on demand. It never stores the elements
• Source. Stream takes Collections, Arrays, or I/O resources as input source.
• Aggregate operations. Stream supports aggregate operations like
• filter,
• map,
• limit,
• reduce,
• find,
• match, and so on.
Class Stream. Source
• Create a source as a list of lines
List<String> strCollection = new ArrayList<>();
[Link]("aa1");
[Link]("bb1");
[Link]("aa2");
[Link]("cc1");
[Link]("aa3");
[Link]("bb2");
[Link]("cc2");
• You can quickly create streams using the calls to [Link]() or
[Link]()
[Link]()
.forEach([Link]::println);
Class Stream. Source
• Characteristic
• Return another stream
• Lazy
• Are familiar to builder pattern
• Should be non-interfering and stateless
• For example
• Collections:
List<T> list;
Stream<T> stream = [Link]();
• Generators:
Stream<Integer> stream = [Link](() -> x++);
• Utilities:
LongStream stream = [Link](0, 100);
Class Stream
• Pipelining. Most of the stream operations return stream itself so that their result can be
pipelined.
• These operations are called intermediate operations and their function is to take input,
process them, and return output to the target.
• collect() method is a terminal operation which is normally present at the end of the
pipelining operation to mark the end of the stream
• Automatic iterations. Stream operations do the iterations internally over the source
elements provided, in contrast to Collections where explicit iteration is required
Stream API.
Aggregate Operations
Filter
• With Java 8, Collection interface has two methods to generate a Stream
• stream() − Returns a sequential stream considering collection as its source
• parallelStream() − Returns a parallel Stream considering collection as its source
• The "filter" method is used to eliminate elements based on a criteria
• The following code segment prints a count of empty strings using filter
List<String> strings = Arrays
.asList("abc", "", "bc", "abcd", "", "jklmn");
List<String> filtered = strings
.stream()
.filter(str -> ![Link]())
.collect([Link]());
[Link](strings);
[Link](filtered);
Filter
• The Filter operation accepts a predicate that filters all elements of the stream
List<String> strCollection = new ArrayList<>();
[Link]("aa1");
[Link]("bb1");
[Link]("aa2");
[Link]("cc1");
//
strCollection
.stream()
.filter((s) -> [Link]("a"))
.forEach([Link]::println);
// "aa1", "aa2"
ForEach. Limit
• limit
• The ‘limit’ method is used to reduce the size of the stream
• forEach
• Stream has provided a new method ‘forEach’ to iterate each element of the stream
• The following code segment shows how to print 10 random numbers using forEach
Random random = new Random();
[Link]()
.limit(10)
.forEach([Link]::println);
Sorted
• sorted
• The "sorted" method is used to sort the stream
• Sorted operation is an intermediate operation
• Оperation returns a sorted representation of the stream.
• By default Items are sorted in the usual way.
List<String> arrList = [Link]("D", "A", "C", "B", "E");
[Link]()
.forEach([Link]::print); // DACBE
[Link]()
.sorted()
.forEach([Link]::print); // ABCDE
Sorted
• sorted
• You can provide your own comparator
Stream<String> arrStream = [Link]("D", "A", "C", "B", "E");
List<String> arrlist = arrStream
.collect([Link]());
arrlist
.stream()
.sorted((s1,s2)->[Link](s1))
.forEach([Link]::print); // EDCBA
[Link]()
.forEach([Link]::print); // DACBE
Map. Distinct
• map
• The ‘map’ method is used to map each element to its corresponding result.
• The following code segment prints unique squares of numbers using map
List<Integer> numbers = [Link](3, 2, 2, 3, 7, 3, 5);
// get list of unique squares
List<Integer> sqrList = numbers
.stream()
.map(i -> i * i)
.distinct()
.collect([Link]());
[Link](sqrList); // [9, 4, 49, 25]
[Link]()
.forEach([Link]::print); // 9 4 49 25
Map
• The following example converts each string to a uppercase string.
List<String> strCollection = Arrays
.asList("aa1", "bb1", "aa2", "cc1", "aa3");
strCollection
.stream()
.forEach([Link]::println); // aa1 bb1 aa2 cc1 aa3
strCollection
.stream()
.map(String::toUpperCase)
.sorted((a, b) -> [Link](a))
.forEach([Link]::println); // CC1 BB1 AA3 AA2 AA1
AnyMatch
• Match
• To check whether a stream satisfies a given predicate, various matching operations are used
• All matching operations are terminal and return a result of type Boolean
List<String> strCollection = Arrays
.asList("aa1", "bb1", "aa2", "cc1", "aa3");
boolean anyStartsWithA =
strCollection
.stream()
.anyMatch((s) -> [Link]("a"));
[Link](anyStartsWithA); // true
AllMatch. NoneMatch
boolean allStartsWithA =
strCollection
.stream()
.allMatch((s) -> [Link]("a"));
[Link](allStartsWithA); // false
boolean noneStartsWithZ =
strCollection
.stream()
.noneMatch((s) -> [Link]("z"));
[Link](noneStartsWithZ); // true
Count
• count
• The Count operation is the final operation and returns the number of elements in the
stream.
• The return type is long.
List<String> strCollection = Arrays
.asList("aa1", "bb1", "aa2", "cc1", "aa3");
long startsWithA =
strCollection
.stream()
.filter((s) -> [Link]("a"))
.count();
[Link](startsWithA); // 3
Count
• count
• The following code segment prints a count of empty strings using filter
List<String> strings =
[Link]("abc", "", "eg", "abcd", "", "jklmn");
// get count of empty string
long count = [Link]()
.filter(string -> [Link]())
.count();
[Link](count); // 2
Reduce
• reduce
• This terminal operation combines all elements of the stream into a single result according to
a given function
• The result is an optional value
List<String> strCollection = Arrays
.asList("aa1", "bb1", "aa2", "cc1", "aa3");
Optional<String> reduced =
strCollection
.stream()
.sorted()
.reduce((s1, s2) -> s1 + "*" + s2);
reduced
.ifPresent([Link]::println); // aa1*aa2*aa3*bb1*cc1
Reduce
• reduce
• Sum and maximum of all elements
List<Integer> numbers = [Link](3, 2, 2, 3, 7, 3, 5);
Integer resSum = numbers
.stream()
.reduce(0, (sum, p) -> sum = sum + p);
[Link](resSum); // 25
Integer resMax = numbers
.stream()
.reduce([Link](0), (max, p) -> max < p ? p : max);
[Link](resMax); // 7
Collectors
• collectors
• Collectors are used to combine the result of processing on the elements of a stream
• Collectors can be used to return a list or a string
List<String> strings = Arrays
.asList("abc", "", "bc", "eg", "abd", "", "jklmn");
List<String> filtered = [Link]()
.filter(string -> ![Link]())
.collect([Link]());
[Link](filtered); // [abc, bc, eg, abd, jklmn]
String mergedString = [Link]()
.filter(string -> ![Link]())
.collect([Link](", "));
[Link](mergedString); // abc, bc, eg, abd, jklmn
Statistics
• statistics
• Statistics collectors are introduced to calculate all statistics when stream processing is being
done
List<Integer> numbers = [Link](3, 2, 2, 3, 7, 3, 5);
IntSummaryStatistics stats = numbers
.stream()
.mapToInt((x) -> x)
.summaryStatistics();
[Link]("Highest " + [Link]()); // 7
[Link]("Lowest " + [Link]()); // 2
[Link]("Sum " + [Link]()); // 25
[Link]("Average " + [Link]());
// 3.5714285714285716
Parallel Processing
• Parallel Processing
• parallelStream() is the alternative of stream for parallel processing
import [Link];
import [Link];
import [Link];
int max = 1000000;
List<String> arr = new ArrayList<>(max);
for (int i = 0; i < max; i++) {
UUID uuid = [Link]();
[Link]([Link]());
}
Parallel Processing. Sorting
long time0 = [Link]();
long count = arr
//.parallelStream() // sequential sort 618 ms
.stream() // parallel sort 333 ms
.sorted()
.count();
[Link](count);
long time1 = [Link]();
long millis = [Link](time1 - time0);
[Link]([Link]("sort %d ms", millis));
Lazy Evaluation
• Streams are lazy because intermediate operations are not evaluated until terminal operation
is invoked
• Each intermediate operation creates a new stream, stores the provided operation/function
and return the new stream
List<Integer> numbers = [Link](3, 2, 2, 3, 7, 3, 5);
[Link]("Original numbers: " + numbers);
// [3, 2, 2, 3, 7, 3, 5]
Stream<Integer> nums = numbers
.stream()
.filter(n -> n > 2);
[Link](1, 10);
[Link]("Updated numbers: " + numbers);
// [3, 10, 2, 3, 7, 3, 5]
[Link]("nums count: " + [Link]()); // 6
Terminal Operations
• reducers: reduce, findFirst, findAny
• collectors: put into collection
• forEach: perform operation over elements
• iterator: get elements one by one
• findFirst/findAny: return appropriate element
• collect: present result in any other data structure
• count: return number of elements in the stream
• anyMatch/noneMatch/allMatch: return true due to condition
• min/max: return min / max element
• forEach /forEachOrdered: applies function for each element
• toArray: return array of values of the stream
Maps
• Associative arrays (map) do not support streams.
• Associative arrays support various useful methods that solve common tasks
Map<Integer, String> map = new HashMap<>();
for (int i = 0; i < 5; i++) {
[Link](i, "num" + i);
}
[Link]((key, value) -> [Link](value + " "));
// num0 num1 num2 num3 num4
[Link](3, (key, value) -> key + "_" + value);
[Link]([Link](3));
// 3_num3
THANKS