Download - Java 8 Stream API. A different way to process collections.

Transcript
Page 1: Java 8 Stream API. A different way to process collections.

Java8 Stream APIA different way to process collectionsDavid Gómez G.@[email protected]

Page 2: Java 8 Stream API. A different way to process collections.

Streams? What’s that?

Page 3: Java 8 Stream API. A different way to process collections.

A Stream is…An convenience method to iterate over

collections in a declarative wayList<Integer>  numbers  =  new  ArrayList<Integer>();for  (int  i=  0;  i  <  100  ;  i++)  {   numbers.add(i); }  

List<Integer> evenNumbers = new ArrayList<>();for (int i : numbers) { if (i % 2 == 0) { evenNumbers.add(i); } }

@dgomezg

Page 4: Java 8 Stream API. A different way to process collections.

A Stream is…An convenience method to iterate over

collections in a declarative wayList<Integer>  numbers  =  new  ArrayList<Integer>();for  (int  i=  0;  i  <  100  ;  i++)  {   numbers.add(i); }  

List<Integer> evenNumbers = numbers.stream() .filter(n -> n % 2 == 0) .collect(toList());

@dgomezg

Page 5: Java 8 Stream API. A different way to process collections.

So… Streams are collections?Not Really

Collections Streams

Sequence of elements

Computed at construction

In-memory data structure

Sequence of elements

Computed at iteration

Traversable only Once

External Iteration Internal Iteration

Finite size Infinite size

@dgomezg

Page 6: Java 8 Stream API. A different way to process collections.

Iterating a CollectionList<Integer> evenNumbers = new ArrayList<>();for (int i : numbers) { if (i % 2 == 0) { evenNumbers.add(i); } }

External Iteration - Use forEach or Iterator - Very verbose Parallelism by manually using Threads - Concurrency is hard to be done right! - Lots of contention and error-prone - Thread-safety

@dgomezg

Page 7: Java 8 Stream API. A different way to process collections.

Iterating a Stream

List<Integer> evenNumbers = numbers.stream() .filter(n -> n % 2 == 0) .collect(toList());

Internal Iteration - No manual Iterators handling - Concise - Fluent API: chain sequence processing Elements computed only when needed

@dgomezg

Page 8: Java 8 Stream API. A different way to process collections.

Iterating a Stream

List<Integer> evenNumbers = numbers.parallelStream() .filter(n -> n % 2 == 0) .collect(toList());

Easily Parallelism - Concurrency is hard to be done right! - Uses ForkJoin - Process steps should be - stateless - independent

@dgomezg

Page 9: Java 8 Stream API. A different way to process collections.

Lambdas &

Method References

Page 10: Java 8 Stream API. A different way to process collections.

@FunctionalInterface

@FunctionalInterfacepublic interface Predicate<T> {

boolean test(T t); !!!!!}

An interface with exactly one abstract method !

!

@dgomezg

Page 11: Java 8 Stream API. A different way to process collections.

@FunctionalInterface

@FunctionalInterfacepublic interface Predicate<T> {

boolean test(T t); ! default Predicate<T> negate() { return (t) -> !test(t); } !}

An interface with exactly one abstract method Could have default methods, though! !

@dgomezg

Page 12: Java 8 Stream API. A different way to process collections.

Lambda TypesBased on abstract method signature from @FunctionalInterface: (Arguments) -> <return type>

@FunctionalInterfacepublic interface Predicate<T> {

boolean test(T t); }

T -> boolean

@dgomezg

Page 13: Java 8 Stream API. A different way to process collections.

Lambda TypesBased on abstract method signature from @FunctionalInterface: (Arguments) -> <return type>

@FunctionalInterfacepublic interface Runnable {

void run(); }

() -> void

@dgomezg

Page 14: Java 8 Stream API. A different way to process collections.

Lambda TypesBased on abstract method signature from @FunctionalInterface: (Arguments) -> <return type>

@FunctionalInterfacepublic interface Supplier<T> {

T get(); }

() -> T

@dgomezg

Page 15: Java 8 Stream API. A different way to process collections.

Lambda TypesBased on abstract method signature from @FunctionalInterface: (Arguments) -> <return type>

@FunctionalInterfacepublic interface BiFunction<T, U, R> {

R apply(T t, U t); }

(T, U) -> R

@dgomezg

Page 16: Java 8 Stream API. A different way to process collections.

Lambda TypesBased on abstract method signature from @FunctionalInterface: (Arguments) -> <return type>

@FunctionalInterfacepublic interface Comparator<T> {

int compare(T o1, T o2); }

(T, T) -> int

@dgomezg

Page 17: Java 8 Stream API. A different way to process collections.

Method ReferencesAllows to use a method name as a lambda Usually better readability !

Syntax: <TargetReference>::<MethodName> !

TargetReference: Instance or Class

@dgomezg

Page 18: Java 8 Stream API. A different way to process collections.

Method References

phoneCall -> phoneCall.getContact()

Method ReferenceLambda

PhoneCall::getContact

() -> Thread.currentThread() Thread::currentThread

(str, c) -> str.indexOf(c) String::indexOf

(String s) -> System.out.println(s) System.out::println

@dgomezg

Page 19: Java 8 Stream API. A different way to process collections.

From Collections to

Streams

Page 20: Java 8 Stream API. A different way to process collections.

Characteristics of A Stream

• Interface to Sequence of elements • Focused on processing (not on storage) • Elements computed on demand

(or extracted from source) • Can be traversed only once • Internal iteration • Parallel Support • Could be Infinite

@dgomezg

Page 21: Java 8 Stream API. A different way to process collections.

Anatomy of a Stream

Source

Intermediate Operations

filter

map

order

function

Final operation

pipe

line

@dgomezg

Page 22: Java 8 Stream API. A different way to process collections.

Anatomy of Stream Iteration1. Start from the DataSource (Usually a

collection) and create the Stream

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); Stream<Integer> numbersStream = numbers.stream();

@dgomezg

Page 23: Java 8 Stream API. A different way to process collections.

Anatomy of Stream Iteration2. Add a chain of intermediate Operations

(Stream Pipeline)Stream<Integer> numbersStream = numbers.stream() .filter(new Predicate<Integer>() { @Override public boolean test(Integer number) { return number % 2 == 0; } }) ! .map(new Function<Integer, Integer>() { @Override public Integer apply(Integer number) { return number * 2; } });

@dgomezg

Page 24: Java 8 Stream API. A different way to process collections.

Anatomy of Stream Iteration2. Add a chain of intermediate Operations

(Stream Pipeline) - Better using lambdas

Stream<Integer> numbersStream = numbers.stream() .filter(number -> number % 2 == 0) .map(number -> number * 2);

@dgomezg

Page 25: Java 8 Stream API. A different way to process collections.

Anatomy of Stream Iteration3. Close with a Terminal Operation

List<Integer> numbersStream = numbers.stream() .filter(number -> number % 2 == 0) .map(number -> number * 2) .collect(Collectors.toList());

•The terminal operation triggers Stream Iteration •Before that, nothing is computed. •Depending on the terminal operation, the stream could be fully traversed or not.

@dgomezg

Page 26: Java 8 Stream API. A different way to process collections.

Stream operations

Page 27: Java 8 Stream API. A different way to process collections.

Operation TypesIntermediate operations • Always return a Stream • Chain as many as needed (Pipeline) • Guide processing of data • Does not start processing • Can be Stateless or Stateful

Terminal operations • Can return an object, a collection, or void • Start the pipeline process • After its execution, the Stream can not be revisited

Page 28: Java 8 Stream API. A different way to process collections.

Intermediate Operations // T -> boolean Stream<T> filter(Predicate<? super T> predicate); ! //T -> R<R> Stream<R> map(Function<? super T, ? extends R> mapper); //(T,T) -> intStream<T> sorted(Comparator<? super T> comparator); Stream<T> sorted(); ! //T -> voidStream<T> peek(Consumer<? super T> action); !Stream<T> distinct();Stream<T> limit(long maxSize);Stream<T> skip(long n);

@dgomezg

Page 29: Java 8 Stream API. A different way to process collections.

Final Operations

Object[] toArray(); void forEach(Consumer<? super T> action); //T -> void<R, A> R collect(Collector<? super T, A, R> collector);!

!java.util.stream.Collectors.toList(); java.util.stream.Collectors.toSet(); java.util.stream.Collectors.toMap(); java.util.stream.Collectors.joining(CharSequence); !!!

@dgomezg

Page 30: Java 8 Stream API. A different way to process collections.

Final Operations (II)

//T,U -> R Optional<T> reduce(BinaryOperator<T> accumulator); //(T,T) -> int Optional<T> min(Comparator<? super T> comparator); //(T,T) -> int Optional<T> max(Comparator<? super T> comparator);long count();!

@dgomezg

Page 31: Java 8 Stream API. A different way to process collections.

Final Operations (y III)

//T -> boolean boolean anyMatch(Predicate<? super T> predicate);boolean allMatch(Predicate<? super T> predicate);boolean noneMatch(Predicate<? super T> predicate);!

@dgomezg

Page 32: Java 8 Stream API. A different way to process collections.

Usage examples - Context

public class Contact { private final String name; private final String city; private final String phoneNumber; private final LocalDate birth; public int getAge() { return Period.between(birth, LocalDate.now()) .getYears(); } //Constructor and getters omitted!}

@dgomezg

Page 33: Java 8 Stream API. A different way to process collections.

Usage examples - Contextpublic class PhoneCall { private final Contact contact; private final LocalDate time; private final Duration duration; ! //Constructor and getters omitted }

Contact me = new Contact("dgomezg", "Madrid", "555 55 55 55", LocalDate.of(1975, Month.MARCH, 26));Contact martin = new Contact("Martin", "Santiago", "666 66 66 66", LocalDate.of(1978, Month.JANUARY, 17));Contact roberto = new Contact("Roberto", "Santiago", "111 11 11 11", LocalDate.of(1973, Month.MAY, 11));Contact heinz = new Contact("Heinz", "Chania", "444 44 44 44", LocalDate.of(1972, Month.APRIL, 29));Contact michael = new Contact("michael", "Munich", "222 22 22 22", LocalDate.of(1976, Month.DECEMBER, 8));List<PhoneCall> phoneCallLog = Arrays.asList( new PhoneCall(heinz, LocalDate.of(2014, Month.MAY, 28), Duration.ofSeconds(125)), new PhoneCall(martin, LocalDate.of(2014, Month.MAY, 30), Duration.ofMinutes(5)), new PhoneCall(roberto, LocalDate.of(2014, Month.MAY, 30), Duration.ofMinutes(12)), new PhoneCall(michael, LocalDate.of(2014, Month.MAY, 28), Duration.ofMinutes(3)), new PhoneCall(michael, LocalDate.of(2014, Month.MAY, 29), Duration.ofSeconds(90)), new PhoneCall(heinz, LocalDate.of(2014, Month.MAY, 30), Duration.ofSeconds(365)), new PhoneCall(heinz, LocalDate.of(2014, Month.JUNE, 1), Duration.ofMinutes(7)), new PhoneCall(martin, LocalDate.of(2014, Month.JUNE, 2), Duration.ofSeconds(315))) ;

@dgomezg

Page 34: Java 8 Stream API. A different way to process collections.

People I phoned in June

phoneCallLog.stream() .filter(phoneCall -> phoneCall.getTime().getMonth() == Month.JUNE) .map(phoneCall -> phoneCall.getContact().getName()) .distinct() .forEach(System.out::println);!

@dgomezg

Page 35: Java 8 Stream API. A different way to process collections.

Seconds I talked in May

Long total = phoneCallLog.stream() .filter(phoneCall -> phoneCall.getTime().getMonth() == Month.MAY) .map(PhoneCall::getDuration) .collect(summingLong(Duration::getSeconds));

@dgomezg

Page 36: Java 8 Stream API. A different way to process collections.

Seconds I talked in MayOptional<Long> total = phoneCallLog.stream() .filter(phoneCall -> phoneCall.getTime().getMonth() == Month.MAY) .map(PhoneCall::getDuration) .reduce(Duration::plus); total.ifPresent(duration -> {System.out.println(duration.getSeconds());} ); !

@dgomezg

Page 37: Java 8 Stream API. A different way to process collections.

Did I phone to Paris?

boolean phonedToParis = phoneCallLog.stream() .anyMatch(phoneCall -> "Paris".equals(phoneCall.getContact().getCity()))!!

@dgomezg

Page 38: Java 8 Stream API. A different way to process collections.

Give me the 3 longest phone calls

phoneCallLog.stream() .filter(phoneCall -> phoneCall.getTime().getMonth() == Month.MAY) .sorted(comparing(PhoneCall::getDuration)) .limit(3) .forEach(System.out::println);

@dgomezg

Page 39: Java 8 Stream API. A different way to process collections.

Give me the 3 shortest ones

phoneCallLog.stream() .filter(phoneCall -> phoneCall.getTime().getMonth() == Month.MAY) .sorted(comparing(PhoneCall::getDuration).reversed()) .limit(3) .forEach(System.out::println);

@dgomezg

Page 40: Java 8 Stream API. A different way to process collections.

Creating Streams

Page 41: Java 8 Stream API. A different way to process collections.

Streams can be created fromCollections Directly from values Generators (infinite Streams) Resources (like files)

Stream ranges

@dgomezg

Page 42: Java 8 Stream API. A different way to process collections.

From collections

use stream()

List<Integer> numbers = new ArrayList<>();for (int i= 0; i < 10_000_000 ; i++) { numbers.add((int)Math.round(Math.random()*100));}

Stream<Integer> evenNumbers = numbers.stream();

or parallelStream()

Stream<Integer> evenNumbers = numbers.parallelStream();

@dgomezg

Page 43: Java 8 Stream API. A different way to process collections.

Directly from Values & ranges

Stream.of("Using", "Stream", "API", "From", “Java8”);

can convert into parallelStreamStream.of("Using", "Stream", "API", "From", “Java8”) .parallel();

@dgomezg

Page 44: Java 8 Stream API. A different way to process collections.

Generators - Functions

Stream<Integer> integers = Stream.iterate(0, number -> number + 2);

This is an infinite Stream!, will never be exhausted!

Stream fibonacci = Stream.iterate(new int[]{0,1}, t -> new int[]{t[1],t[0]+t[1]}); fibonacci.limit(10) .map(t -> t[0]) .forEach(System.out::println);

@dgomezg

Page 45: Java 8 Stream API. A different way to process collections.

Generators - Functions

Stream<Integer> integers = Stream.iterate(0, number -> number + 2);

This is an infinite Stream!, will never be exhausted!

Stream fibonacci = Stream.iterate(new int[]{0,1}, t -> new int[]{t[1],t[0]+t[1]}); fibonacci.limit(10) .map(t -> t[0]) .forEach(System.out::println);

@dgomezg

Page 46: Java 8 Stream API. A different way to process collections.

From Resources (Files)

Stream<String> fileContent = Files.lines(Paths.get(“readme.txt”));

Files.lines(Paths.get(“readme.txt”)) .flatMap(line -> Arrays.stream(line.split(" "))) .distinct() .count()); !

Count all distinct words in a file

@dgomezg

Page 47: Java 8 Stream API. A different way to process collections.

Parallelism

Page 48: Java 8 Stream API. A different way to process collections.

Parallel Streams

use stream()

List<Integer> numbers = new ArrayList<>();for (int i= 0; i < 10_000_000 ; i++) { numbers.add((int)Math.round(Math.random()*100));}

//This will use just a single thread Stream<Integer> evenNumbers = numbers.stream();

or parallelStream()//Automatically select the optimum number of threads Stream<Integer> evenNumbers = numbers.parallelStream();

@dgomezg

Page 49: Java 8 Stream API. A different way to process collections.

Let’s test it

use stream()

!for (int i = 0; i < 100; i++) { long start = System.currentTimeMillis(); List<Integer> even = numbers.stream() .filter(n -> n % 2 == 0) .sorted() .collect(toList()); System.out.printf( "%d elements computed in %5d msecs with %d threads\n”, even.size(), System.currentTimeMillis() - start, Thread.activeCount());} 5001983 elements computed in 828 msecs with 2 threads 5001983 elements computed in 843 msecs with 2 threads 5001983 elements computed in 675 msecs with 2 threads 5001983 elements computed in 795 msecs with 2 threads

@dgomezg

Page 50: Java 8 Stream API. A different way to process collections.

Let’s test it

use stream()

!for (int i = 0; i < 100; i++) { long start = System.currentTimeMillis(); List<Integer> even = numbers.parallelStream() .filter(n -> n % 2 == 0) .sorted() .collect(toList()); System.out.printf( "%d elements computed in %5d msecs with %d threads\n”, even.size(), System.currentTimeMillis() - start, Thread.activeCount());}

4999299 elements computed in 225 msecs with 9 threads 4999299 elements computed in 230 msecs with 9 threads 4999299 elements computed in 250 msecs with 9 threads

@dgomezg

Page 51: Java 8 Stream API. A different way to process collections.

Enough, for now, But this is just the beginning

Thank You.

@[email protected]

www.adictosaltrabajlo.com