47
Harnessing the power of Java 8 Streams Praveer Gupta

Harnessing the Power of Java 8 Streams

Embed Size (px)

Citation preview

Harnessing the power of

Java 8 StreamsPraveer Gupta

Default Methods

Lambda Expressions

Streams

Date-Time API

CompletableFuture

Optional

helps to make a strategic decision about what programming language should be adopted

when starting to build a new software system

Java 8 released on Mar 18, 2014

–Robert C. Martin

“There are two parts to learning craftsmanship: knowledge and work.

You must gain the knowledge of principles, patterns, practices, and heuristics that a craftsman knows,

and you must also grind that knowledge

into your fingers, eyes, and gut by working hard and

practicing..”

Stream

Intermediate Operators

Terminal Operators

Harnessing the power Java 8 Streams

Programming Paradigm Concepts

Stream

Collectorscollectreduce

Intermediate Operators

Terminal Operators

computation creation Primitive Streams

stateless/stateful

Lazy & Short-Circuited

Harnessing the power Java 8 Streams

Declarative Programming

Internal Iteration

chained categories

Programming Paradigm Concepts

Harnessing the power Java 8 Streams

Declarative Programming

Internal Iteration

Programming Paradigm Concepts

DeclarativeSELECT NAME FROM PERSON WHERE AGE > 18;

List<String> adults = group.stream() .filter(p -> p.age() > 18) .map(Person::name) .collect(toList());

You declare what the program has to do and the library takes care of how to do it

DeclarativeFocuses on

What

List<String> adults = new ArrayList<>();for (Person person: group) if (person.age() > 18) adults.add(person.name());

Focuses on How and

What

vs Imperative

List<String> adults = group.stream() .filter(p -> p.age() > 18) .map(Person::name) .collect(toList());

Old way vs New wayfinal List<String> names = new ArrayList<>();for (Person p : group) if (p.age() > 18) names.add(p.name());return names;

return group.stream() .filter(p -> p.age() > 18) .map(Person::name) .collect(toList());

List<String> namesOfAdultsInTheGroup(List<Person> group) {

}

External Iteration

Internal Iteration

new declarative wayold imperative way

List<String> namesOfAdultsInTheGroup(List<Person> group) {

}

Application of Streamsinterface CustomerRepository { List<Customer> findByCity(String cityName); }

API Definition

Application of Streamsinterface CustomerRepository { Stream<Customer> findByCity(String cityName);}

try (Stream<String> stream = repository.findByCity(“Pune")) { return stream.filter(…).count();}

API Definition

Usage of the API

Consume items as they arrive Efficient memory usage

Streamcomputation creation

Harnessing the power Java 8 Streams

Declarative Programming

Internal Iteration

Programming Paradigm Concepts

Streama read-only sequence of elements

computational operations that will be performed in aggregate

Collectionefficient management of elements

efficient access to elements

Stream Pipeline

StreamTerminal Operator

Ope

rato

r 1

Ope

rato

r 2

Ope

rato

r 3

Intermediate Operators

(Zero or more)

StreamTerminal Operator

Ope

rato

r 1

Ope

rato

r 2

Ope

rato

r 3

Stream Pipeline

List<String> namesOfAdultsInTheGroup(List<Person> group) { return }

group.stream() .filter(p -> p.age() > 18) .map(Person::name) .collect(Collectors.toList());

names

group

Stream<Person> Archer(22 years)

Barbara(17 years)

Daisy(25 years)

is person’s age > 18 ?

Stream<Person>

Stream<String>

map to name of person

collect into a list

group.stream()

.filter(p -> p.age() > 18)

.map(Person::name)

.collect( Collectors.toList());

Archer(22 years)

Barbara(17 years)

Daisy(25 years)

Archer(22 years)

Daisy(25 years)

Archer Daisy

Archer Daisy

Creating StreamFrom CollectionList<String> list = Arrays.asList("a", "b", "c"); Stream<String> stream = list.stream();

String[] array = {"a", "b", "c"};Stream<String> stream = Arrays.stream(array);

Creating StreamFrom an I/O Channeltry (Stream<Path> stream = Files.list(Paths.get(“.”));) { … }

Stream interface extends AutoCloseable

Creating StreamUsing Generator FunctionsStream<Double> stream = Stream.generate(Math::random);

Stream<Integer> stream = Stream.iterate(0, n -> n + 3);

Both are unbounded

Parallel StreamFrom CollectionList<String> list = Arrays.asList("a", "b", "c"); Stream<String> stream = list.parallelStream();

From another StreamStream<String> parallelStream = stream.parallel();

List<String> list = Arrays.asList("a", "b", "c"); Stream<String> stream = list.parallelStream();

Stream<String> parallelStream = stream.parallel();

Parallel StreamHow does it work?

Splitting CombiningParallel Processing

Parallel StreamPerformance ImpactAlways measure performance before using parallel

Stream size predictability & Amount of data

Decomposability of source data structure

Computational Cost

Stream

Intermediate Operators

computation creation

stateless/stateful

Harnessing the power Java 8 Streams

Declarative Programming

Internal Iteration

chained categories

Programming Paradigm Concepts

Intermediate Operators

StreamTerminal Operator

Ope

rato

r 1

Ope

rato

r 2

Ope

rato

r 3

Intermediate Operators

(Zero or more)

Intermediate OperatorsCategoriesStream<Integer> stream = Stream.of(3, 2, 1);

Stream<Integer> filtered = stream.filter(n -> n % 2 == 0);

Filtering

Type of stream remains the same

Stream<Integer> filtered = stream.filter(n -> n % 2 == 0);

Intermediate OperatorsCategoriesStream<Integer> stream = Stream.of(3, 2, 1);

Stream<String> mapped = stream.map(Object::toString);

Mapping

Type of stream gets altered (here Integer to String)

Stream<String> mapped = stream.map(Object::toString);

Intermediate OperatorsCategoriesStream<Integer> stream = Stream.of(3, 2, 1);

Stream<Integer> sliced = stream.limit(1);

Slicing

Type of stream remains the same

Stream<Integer> sliced = stream.limit(1);

Stream.of(1, 2, 3, 4) .peek(System.out::println) .filter(n -> n % 2 == 0);

Intermediate OperatorsLaziness

Stream.of(1, 2, 3, 4) .peek(System.out::println) .filter(n -> n % 2 == 0) .collect(toList());

Stream.of(1, 2, 3, 4) .peek(System.out::println) .filter(n -> n % 2 == 0);

Terminal operator is required to start stream processing

Stream.of(1, 2, 3, 4) .peek(System.out::println) .filter(n -> n % 2 == 0) .collect(toList());

Peek will not print anything

Intermediate OperatorsShort-CircuitingStream.of(1, 2, 3, 4) .peek(System.out::println) .filter(n -> n % 2 == 0) .findFirst();

Stream will get short-circuited after the first element is found

Peek will print only 1 & 2

Stream.of(1, 2, 3, 4) .peek(System.out::println) .filter(n -> n % 2 == 0) .findFirst();

Intermediate OperatorsStateless

vs Stateful

Stream.of(1, 2, 3, 4) .peek(System.out::println) .filter(n -> n % 2 == 0) .findFirst();

Stream.of(1, 2, 3, 4) .peek(System.out::println) .sorted(Comparator.reverseOrder()) .filter(n -> n % 2 == 0) .findFirst();

Will cause the whole stream

to be traversed

All operations are done on current value

in stream

Stream.of(1, 2, 3, 4) .peek(System.out::println) .sorted(Comparator.reverseOrder()) .filter(n -> n % 2 == 0) .findFirst();

Stream

Collectorscollectreduce

Intermediate Operators

Terminal Operators

computation creation

stateless/stateful

Lazy & Short-Circuited

Harnessing the power Java 8 Streams

Declarative Programming

Internal Iteration

chained categories

Programming Paradigm Concepts

Terminal Operators

Single Value Collection

Reduce Operation Collect Operation

Terminal Operatorsreduce

<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);

Stream.of(3, 2, 1).reduce(0, Integer::sum, Integer::sum);

Stream.of(3, 2, 1).reduce(0, Integer::sum);

Immutable Reduction Process

<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);

<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);

<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);

Terminal Operatorsreduce utility methodsOptional<Integer> max = Stream.of(1, 2, 3) .max(Comparator.naturalOrder());

boolean result = Stream.of(1, 2, 3) .anyMatch(n -> n > 2);

Optional<Integer> max = Stream.of(1, 2, 3) .max(Comparator.naturalOrder());

boolean result = Stream.of(1, 2, 3) .anyMatch(n -> n > 2);

Terminal OperatorscollectMutable Reduction Process

Accumulates elements into a mutable result container

Stream.of("a", "b", "c").reduce("", String::concat);

String copying = Low Performance !!

Terminal Operatorscollect

<R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner);

StringBuilder builder = Stream.of("a", "b", "c").collect( StringBuilder::new, StringBuilder::append, StringBuilder::append);

StringBuilder is the mutable container here

Terminal OperatorsCollectors class

String joinedString = Stream.of(“a", "b", “c") .collect(Collectors.joining(", "));

IntSummaryStatistics statistics = group.stream() .collect(Collectors.summarizingInt(Person::age));

IntSummaryStatistics{count=10, sum=280, min=26, average=28.000000, max=30}

Output: a, b, c

String joinedString = Stream.of(“a", "b", “c") .collect(Collectors.joining(", "));

IntSummaryStatistics statistics = group.stream() .collect(Collectors.summarizingInt(Person::age));

Terminal OperatorsDownstream Collectors

Map<Integer, List<Person>> result = group.stream() .collect(Collectors.groupingBy(Person::age, Collectors.toList()));

Map<Integer, List<Person>> result = group.stream() .collect(groupingBy(Person::age));

Divide into different age groups

Terminal OperatorsDownstream Collectors

Map<Integer, Long> result = group.stream() .collect(groupingBy(Person::age, counting()));

Map<Integer, List<String>> result = group.stream() .collect(groupingBy(Person::age, mapping(Person::name, toList())));

Count of people in each age group

Names on people in each age group

Stream

Collectorscollectreduce

Intermediate Operators

Terminal Operators

computation creation Primitive Streams

stateless/stateful

Lazy & Short-Circuited

Harnessing the power Java 8 Streams

Declarative Programming

Internal Iteration

chained categories

Programming Paradigm Concepts

Primitive StreamsIntStream DoubleStreamLongStream

Avoid boxing and unboxing costs

Numeric operations are available

XXXFunction

XXXPredicateXXXSupplierXXXConsumer

XXXToXXXFunction

Primitive Functional Interfaces

Primitive StreamsCreating from factory methods

IntStream intStream = IntStream.range(1, 10);

DoubleStream doubleStream = DoubleStream.of(1.0, 2.0);

LongStream longStream = LongStream.iterate(0L, n -> n + 4);

Generating a range of numbers

Stream of known set of numbers

Stream using iterative application of a function

Stream<Person> stream = group.stream();

Primitive StreamsObtaining from Stream<T>

mapToXXX flatMapToXXX

IntStream intStream = stream.mapToInt(Person::age);

OptionalDouble averageAge = intStream.average();Specialized methods on Primitive Streams

IntStream intStream = stream.mapToInt(Person::age);

Primitive StreamsConverting back to Stream<T>

Stream<Integer> boxed = IntStream.of(1, 2, 3).boxed();Stream<Integer> boxed = IntStream.of(1, 2, 3).boxed();

Stream

Collectorscollectreduce

Intermediate Operators

Terminal Operators

computation creation Primitive Streams

stateless/stateful

Lazy & Short-Circuited

Harnessing the power Java 8 Streams

Declarative Programming

Internal Iteration

chained categories

Programming Paradigm Concepts

Questions?

http://praveer09.github.io@praveerguptapraveer