Java
Stream
API
softserve
Agenda
•Class java.util.Stream
•Stream API. Aggregate Operations
•Case Studies
Class
java.util.Stream
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<>();
strCollection.add("aa1");
strCollection.add("bb1");
strCollection.add("aa2");
strCollection.add("cc1");
strCollection.add("aa3");
strCollection.add("bb2");
strCollection.add("cc2");
• You can quickly create streams using the calls to Collection.stream() or
Collection.parallelStream()
strCollection.stream()
.forEach(System.out::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 = list.stream();
• Generators:
Stream<Integer> stream = Stream.generate(() -> x++);
• Utilities:
LongStream stream = LongStream.range(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 -> !str.isEmpty())
.collect(Collectors.toList());
System.out.println(strings);
System.out.println(filtered);
Filter
• The Filter operation accepts a predicate that filters all elements of the stream
List<String> strCollection = new ArrayList<>();
strCollection.add("aa1");
strCollection.add("bb1");
strCollection.add("aa2");
strCollection.add("cc1");
//
strCollection
.stream()
.filter((s) -> s.startsWith("a"))
.forEach(System.out::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();
random.ints()
.limit(10)
.forEach(System.out::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 = Arrays.asList("D", "A", "C", "B", "E");
arrList.stream()
.forEach(System.out::print); // DACBE
arrList.stream()
.sorted()
.forEach(System.out::print); // ABCDE
Sorted
• sorted
• You can provide your own comparator
Stream<String> arrStream = Stream.of("D", "A", "C", "B", "E");
List<String> arrlist = arrStream
.collect(Collectors.toList());
arrlist
.stream()
.sorted((s1,s2)->s2.compareTo(s1))
.forEach(System.out::print); // EDCBA
arrlist.stream()
.forEach(System.out::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 = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
// get list of unique squares
List<Integer> sqrList = numbers
.stream()
.map(i -> i * i)
.distinct()
.collect(Collectors.toList());
System.out.println(sqrList); // [9, 4, 49, 25]
sqrList.stream()
.forEach(System.out::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(System.out::println); // aa1 bb1 aa2 cc1 aa3
strCollection
.stream()
.map(String::toUpperCase)
.sorted((a, b) -> b.compareTo(a))
.forEach(System.out::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) -> s.startsWith("a"));
System.out.println(anyStartsWithA); // true
AllMatch. NoneMatch
boolean allStartsWithA =
strCollection
.stream()
.allMatch((s) -> s.startsWith("a"));
System.out.println(allStartsWithA); // false
boolean noneStartsWithZ =
strCollection
.stream()
.noneMatch((s) -> s.startsWith("z"));
System.out.println(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) -> s.startsWith("a"))
.count();
System.out.println(startsWithA); // 3
Count
• count
• The following code segment prints a count of empty strings using filter
List<String> strings =
Arrays.asList("abc", "", "eg", "abcd", "", "jklmn");
// get count of empty string
long count = strings.stream()
.filter(string -> string.isEmpty())
.count();
System.out.println(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(System.out::println); // aa1*aa2*aa3*bb1*cc1
Reduce
• reduce
• Sum and maximum of all elements
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
Integer resSum = numbers
.stream()
.reduce(0, (sum, p) -> sum = sum + p);
System.out.println(resSum); // 25
Integer resMax = numbers
.stream()
.reduce(numbers.get(0), (max, p) -> max < p ? p : max);
System.out.println(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 = strings.stream()
.filter(string -> !string.isEmpty())
.collect(Collectors.toList());
System.out.println(filtered); // [abc, bc, eg, abd, jklmn]
String mergedString = strings.stream()
.filter(string -> !string.isEmpty())
.collect(Collectors.joining(", "));
System.out.println(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 = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
IntSummaryStatistics stats = numbers
.stream()
.mapToInt((x) -> x)
.summaryStatistics();
System.out.println("Highest " + stats.getMax()); // 7
System.out.println("Lowest " + stats.getMin()); // 2
System.out.println("Sum " + stats.getSum()); // 25
System.out.println("Average " + stats.getAverage());
// 3.5714285714285716
Parallel Processing
• Parallel Processing
• parallelStream() is the alternative of stream for parallel processing
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
int max = 1000000;
List<String> arr = new ArrayList<>(max);
for (int i = 0; i < max; i++) {
UUID uuid = UUID.randomUUID();
arr.add(uuid.toString());
}
Parallel Processing. Sorting
long time0 = System.nanoTime();
long count = arr
//.parallelStream() // sequential sort 618 ms
.stream() // parallel sort 333 ms
.sorted()
.count();
System.out.println(count);
long time1 = System.nanoTime();
long millis = TimeUnit.NANOSECONDS.toMillis(time1 - time0);
System.out.println(String.format("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 = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
System.out.println("Original numbers: " + numbers);
// [3, 2, 2, 3, 7, 3, 5]
Stream<Integer> nums = numbers
.stream()
.filter(n -> n > 2);
numbers.set(1, 10);
System.out.println("Updated numbers: " + numbers);
// [3, 10, 2, 3, 7, 3, 5]
System.out.println("nums count: " + nums.count()); // 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++) {
map.putIfAbsent(i, "num" + i);
}
map.forEach((key, value) -> System.out.print(value + " "));
// num0 num1 num2 num3 num4
map.computeIfPresent(3, (key, value) -> key + "_" + value);
System.out.print(map.get(3));
// 3_num3
THANKS