83
Java 8 (JSR 335): Lambdas, Streams, Funktionale Interfaces, Default-Methoden Martin Lehmann und Markus Günther, Accso GmbH Vortrag auf der Java User Group Frankfurt am 26. März 2014

Java 8 (JSR335): Lambdas, Streams, Funktionale Interfaces, Default-Methoden

Embed Size (px)

Citation preview

Java 8 (JSR 335): Lambdas, Streams, Funktionale Interfaces, Default-MethodenMartin Lehmann und Markus Günther, Accso GmbH

Vortrag auf der Java User Group Frankfurt am 26. März 2014

2

Co

pyr

igh

201

4 A

ccso

Gm

bHAbout me

� 42 Jahre

� Studium der Informatik an der Universität Würzburg

� Berufsstart 1997

� Einstieg bei sd&m 2001

� Verschiedene industrielle Softwareprojekte als Entwickler, Architekt, Projektmanager, Berater

� Mitarbeit bei sd&m Research zu Quasar von 2006-2007

� Seit 2010 Mitglied der Geschäftsleitung und CTO beim IT-DienstleisterAccso – Accelerated Solutions GmbH in Darmstadtwww.accso.de

� Interne Weiterbildung, Praktikum zu Java 8 im Sommer 2013 auf Basis JDK1.8b86 und IntelliJ 12

� Veröffentlichungen zu Java 7 u. Java 8:

3

Co

pyr

igh

201

4 A

ccso

Gm

bHDer steile und steinige Weg zu Java 8

(c) Helge Schütt, 2013, Hafencity HH

Java 8 GA seit

18. März 2014!

Java 6: Dezember 2006

Java 7: Juli 2011

Größte Neuerung und erste

wesentliche Sprachänderung

seit den Generics in Java 5:

Lambda Expressions

Umfrage von Typesafe im Feb‘14:

Größte Vorfreude bei Entwicklern

Lambdas mit 83%

Collections/Streams mit 30%

4

Co

pyr

igh

201

4 A

ccso

Gm

bHAgenda: Was kommt in Java 8 mit JSR 335?

Lambda-Ausdrücke

Verbesserte Typinferenz

Methodenreferenzen

Collections & Streams

Filter/Map/Reduce, Parallelisierung

Funktionale InterfacesDefault-

Methoden

in

Interfacesjava.util.function

5

Co

pyr

igh

201

4 A

ccso

Gm

bHAgenda: Was kommt in Java 8 mit JSR 335?

Lambda-Ausdrücke

Verbesserte Typinferenz

Methodenreferenzen

Collections & Streams

Filter/Map/Reduce, Parallelisierung

Funktionale Interfaces

Default-

Methoden

in

Interfaces

java.util.function

Nötig, um in Java 8

die Lambda-Ausdrücke für

Java-Collections nutzbar zu

machen bei gleichzeitiger

Abwärtskompatibilität.

6

Co

pyr

igh

201

4 A

ccso

Gm

bH

Was passiert, wenn ich einem Interface eine Methode hinzufügen möchte?

public class FooImpl implements Fooable {void foo() {System.out.println(“Hello!”);

}}

class App { // Version 1public static void main(...) {new FooImpl().foo();

}}

public interface Fooable {void foo(); // Version 1

}

7

Co

pyr

igh

201

4 A

ccso

Gm

bH

Was passiert, wenn ich einem Interface eine Methode hinzufügen möchte?

public interface Fooable {void foo(); // Version 2void bar();

}

class App { // Version 2public static void main(...) {new FooImpl().bar();

}}

public class FooImpl implements Fooable {void foo() {System.out.println(“Hello!”);

}}

class App { // Version 1public static void main(...) {new FooImpl().foo();

}}

public interface Fooable {void foo(); // Version 1

}

java.lang.NoSuchMethodError: Fooable.bar()V

8

Co

pyr

igh

201

4 A

ccso

Gm

bH

Unterscheide: Laufzeit- und Quelltextsicht bei Kompatibilitätsbetrachtungen von modifizierten Klassen.

Laufzeitsicht� Hinzufügen einer Interface-Methode ist binärkompatibel

� Wir können Fooable (Version 2) kompilieren …

� … und alle Class-Dateien, die das Interface nutzen, linken weiterhin

� VM „webt“ fehlende Methode zur Link-Zeit in die Klassen –diese Methode wirft NoSuchMethodError

Quelltextsicht� FooImpl muss Vertrag von Fooable (Version 2) erfüllen

� Hinzufügen einer Interface-Methode ist nicht abwärtskompatibel

Default-Methodsin Java 8

� Default-Methode ist eine Methodenimplementierung im Interface

� VM „webt“ Default-Methode zur Link-Zeit in implementierende Klasse

� Schlüsselwort default zeigt Default-Methode im Interface an

public interface Fooable {void foo();default void bar() {/* Default-Implementierung im Interface */

}}

Schlüsselwort

default

9

Co

pyr

igh

201

4 A

ccso

Gm

bH

Zur Compile-Zeit muss eindeutig sein, welche Default-Methode zur Link-Zeit in Implementierungen genutzt wird.

Regel 1Class wins

� (Super-)Klasse hat immer Vorrang vor Interface („Class wins“)

� Gilt für konkrete und abstrakte Methoden in einer (Super-)Klasse

� Gibt es keine überschriebene Default-Methode in einer (Super)-Klasse, dann greift Regel 2

Regel 2Subtype wins

� Spezifischstes Interface mit Default-Methode wählen („Subtype wins“)

� Beispiel: Default-Methode in List<E> hat Vorrang vor Default-Methode in Collection<E>

� Gibt es mehrere gleich-spezifische Interfaces, dann greift Regel 3

Regel 3� Konflikt kann nicht durch den Compiler aufgelöst werden

� Behandle Default-Methode, als wäre sie abstrakt

� Erfordert Implementierung in konkreter Klasse

Nicht eindeutig?!� Klasse kann mehrere Default-Methoden mit gleicher Signatur haben

� … weil Interface von anderem Interface erben kann

� … weil eine Klasse mehrere Interfaces implementieren kann

� Welche Default-Methode wählt der Compiler?

10

Co

pyr

igh

201

4 A

ccso

Gm

bH

Bei eindeutiger Methodenresolution wählt der Compiler die spezifischste Implementierung einer Default-Methode.

public interface A {default void sayHello() {System.out.println(“Hallo aus A”);

}}

public interface B extends A {default void sayHello() {System.out.println(“Hallo aus B”);

}}

public class C1 implements A, B {public static void main(String[] args) {C1 c = new C1();c.sayHello();

}}

Ergebnis:

„Hallo aus B“

B.sayHello ist

spezifischste

Implementierung

aus Sicht von C1!

11

Co

pyr

igh

201

4 A

ccso

Gm

bH

Der Compiler zeigt einen Kompilierfehler an, falls er die spezifischste Default-Methode nicht zuordnen kann.

public interface D {default void sayHello() {System.out.println(“D”);

}}

public interface E {default void sayHello() {System.out.println(“E”);

}}

public class C2 implements D, E {public static void main(String[] args) {C2 c = new C2();c.sayHello();

}} C2 kompiliert

nicht, da D und E

„gleich-spezifisch“

12

Co

pyr

igh

201

4 A

ccso

Gm

bHKonflikte muss der Entwickler explizit auflösen.

public interface D {default void sayHello() {System.out.println(“D”);

}}

public interface E {default void sayHello() {System.out.println(“E”);

}}

public class C2 implements D, E {public static void main(String[] args) {C2 c = new C2();c.sayHello();

}

@Overridepublic void sayHello() {D.super.sayHello();

}}

C2 kompiliert nun

wg. expliziter

Auflösung

13

Co

pyr

igh

201

4 A

ccso

Gm

bHIst das nicht Mehrfachvererbung?

Ja, aber…� Mehrfachvererbung gibt es schon immer in Java:

� Klasse B kann von Klasse A ableiten und

� … zusätzliche Interfaces implementieren

� Aber das ist nicht Mehrfachvererbung von Zustand… sondern Mehrfachvererbung von Verhalten!

� Achtung:

� Default-Methoden sind virtuelle Methoden

� Spezifischste Default-Methode wird aus Sicht des dynamischen Typs ermittelt

� Kann zu überraschenden Resultaten führen

14

Co

pyr

igh

201

4 A

ccso

Gm

bH

Default-Methoden: Mehrfachvererbung kann zu überraschenden Ergebnissen führen. lc01_interfaces_defaultmethods

Image courtesy of phanlop88 / FreeDigitalPhotos.net

15

Co

pyr

igh

201

4 A

ccso

Gm

bH

public interface C extends A {default void result(int i, int j) {System.out.println(“C.result = “ + calc(i, i));

}}

public interface B extends A {default int calc(int i, int j) {return i * j;

}}

public interface C extends A {default void result(int i, int j) {System.out.println(“C.result = “ + calc(i, j));

}}

public interface B extends A {default int calc(int i, int j) {return i * j;

}}

Die Mehrfachvererbung von Verhalten kann zu überraschenden Ergebnissen führen.

public interface A {default void result(int i, int j) {System.out.println(“A.result = “ + calc(i, j));

}default int calc(int i, int j) { return i + j; }

}

public class Impl implements B, C {public static void main(String[] args) {new Impl().result(3, 4); // Ergebnis?

}}

Ergebnis:

12,

nicht 7

16

Co

pyr

igh

201

4 A

ccso

Gm

bH

Ende

Image courtesy of phanlop88 / FreeDigitalPhotos.net

17

Co

pyr

igh

201

4 A

ccso

Gm

bHAgenda: Was kommt in Java 8 mit JSR 335?

Lambda-Ausdrücke

Verbesserte Typinferenz

Methodenreferenzen

Collections & Streams

Filter/Map/Reduce, Parallelisierung

Funktionale InterfacesDefault-

Methoden

in

Interfacesjava.util.function

18

Co

pyr

igh

201

4 A

ccso

Gm

bHLambda-Ausdrücke in Java 8

(Object o) -> o.toString()

Eigenschaften� Lambda-Ausdruck ist eine anonyme Methode

� Hat Parameter, einen Rückgabetyp, einen Körper

Operator für Lambda-Ausdrücke

Parameterliste Körper

� Kann den umschließenden Scope nutzen (variable capture)

(Person p) -> p.getName().equals(name)

� Kann existierende Methoden referenzieren

Object::toString

Operator für Methodenreferenzen

19

Co

pyr

igh

201

4 A

ccso

Gm

bHLambda-Ausdrücke in Java 8

Ohne Typangabex -> x + 1;

Mit Typangabe(Integer i) -> list.add(i);

Block, explizites return

(Integer a, Integer b) -> {if (a < b) return a + b;return a;

}

Ohne Parameter() -> System.out.println(“Hallo Lambda!”);

20

Co

pyr

igh

201

4 A

ccso

Gm

bHWozu benötigen wir Lambda-Ausdrücke in Java?

Code-as-Data� Verhalten in Form eines Lambda-Ausdrucks kodieren…

� … und als Parameter an eine generische Methode wie map oder filter übergeben

� Entkoppelt Kodierung von Ausführung (vgl. innere Klassen)

Lesbarkeit� Code ist Kommunikationsmittel

� Beschreibt das „was“, nicht das „wie“

� Mächtigere, ausdrucksstärkere APIs möglich

FunktionalerProgrammierstil

� Höherer Abstraktionsgrad durch Funktionen höherer Ordnung

� Eleganter, kürzer, prägnanter

� Keine Seiteneffekte

21

Co

pyr

igh

201

4 A

ccso

Gm

bHLambda-Ausdrücke ersetzen anonyme innere Klassen.

Bedeutung für bisherigen Code

� Äquivalenz zwischen Lambdas und anonyme innerer Klasse mit Single-Abstract-Method (SAM).

� Beispiele: Runnable, Comparator, ActionListener, …

Lambda� Führt keinen zusätzlichen Scope ein

� Damit kein Shadowing von Bezeichnern

� this bezieht sich auf die umschließende Klasse

� Ein return aber nicht!

Anonyme innere Klasse

� Führt zusätzlichen Scope ein

� Daher Shadowing von Bezeichnern in verschiedenen Scopes

� this bezieht sich auf anonyme innere Klasse,umschließender Kontext über ClassName.this erreichbar

22

Co

pyr

igh

201

4 A

ccso

Gm

bHVon Anonymous Inner Classes zu Lambdas

lc02_anoninnerclass

Image courtesy of phanlop88 / FreeDigitalPhotos.net

23

Co

pyr

igh

201

4 A

ccso

Gm

bH

Anonyme innere Klassen lassen sich einfach in äquivalente Lambda-Ausdrücke migrieren.

Comparator<Integer> sortAscending = new Comparator<Integer>() {@Overridepublic int compare(Integer a, Integer b) {return a-b;

}};

Typischer Code für einen Comparator, der Ganzzahlen vergleicht …

Comparator<Integer> sortAscending = new Comparator<Integer>() {@Overridepublic int compare(Integer a, Integer b) {return a-b;

}};

Warum muss ich compare

überschreiben? Es ist die

einzige Methode des Interface,

mein Vorhaben ist doch klar?

24

Co

pyr

igh

201

4 A

ccso

Gm

bH

Anonyme innere Klassen lassen sich einfach in äquivalente Lambda-Ausdrücke migrieren.

Comparator<Integer> sortAscending = new Comparator<Integer>() {@Overridepublic int compare(Integer a, Integer b) {return a-b;

}};

Comparator<Integer> sortAscending = (Integer a, Integer b) -> {return a-b;

};

Reduktion auf das Wesentliche:

Eingabedaten und Code-Block

Die Definition eines Comparator-Interfaces erfordert viel Boilerplate-Code

Comparator<Integer> sortAscending = new Comparator<Integer>() {@Overridepublic int compare(Integer a, Integer b) {return a-b;

}};

25

Co

pyr

igh

201

4 A

ccso

Gm

bH

Anonyme innere Klassen lassen sich einfach in äquivalente Lambda-Ausdrücke migrieren.

Comparator<Integer> sortAscending = (Integer a, Integer b) -> {return a-b;

};

Comparator<Integer> sortAscending = (Integer a, Integer b) -> a-b;

Comparator<Integer> sortAscending = (a, b) -> a-b;

… wir führen allerdings immer noch redundante Informationen mit …

Block-Schreibweise und

explizites return entfernen

Der Compiler kann Typ-

informationen inferieren

26

Co

pyr

igh

201

4 A

ccso

Gm

bHEnde

Image courtesy of phanlop88 / FreeDigitalPhotos.net

27

Co

pyr

igh

201

4 A

ccso

Gm

bHPerformance?

Entnommen aus Joseph D. Darcy‘s Präsentation On the road to JDK 8 @ Devoxx 2012, Antwerpen

Operationen / s Single-threaded Multi-threaded

(saturiert)

Faktor

Anonyme innere Klasse 160 1407 8,8

Capturing Lambda 160 1400 8,8

Non Capturing Lambda 636 23201 36,4

Performance� Lambda-Funktionen sind im Worst-Case so effizient wie

anonyme innere Klassen, im Best-Case deutlich schneller.

� Oracle Performance Team hat Effizienzbetrachtungen durchgeführt (Daten von Herbst 2012).

Lambdas ohne Zugriff

auf umschließenden

Kontext: Um Faktoren

schneller!

28

Co

pyr

igh

201

4 A

ccso

Gm

bHPerformance?

Entnommen aus Joseph D. Darcy‘s Präsentation On the road to JDK 8 @ Devoxx 2012, Antwerpen

Kosten Anonyme innere Klasse Lambda-Ausdruck

Link-Zeit Laden der Klasse Einmalige Kosten beim Aufsetzender capture

Konstruktion Konstruktor aufrufen Erzeugung des Lambda-Ausdrucks

Aufruf invokeinterface invokedynamic

29

Co

pyr

igh

201

4 A

ccso

Gm

bHAgenda: Was kommt in Java 8 mit JSR 335?

Lambda-Ausdrücke

Verbesserte Typinferenz

Methodenreferenzen

Collections & Streams

Filter/Map/Reduce, Parallelisierung

Funktionale InterfacesDefault-

Methoden

in

Interfacesjava.util.function

30

Co

pyr

igh

201

4 A

ccso

Gm

bH

Funktionale Interfaces repräsentieren das Typsystem für einen Lambda-Ausdruck.

Funktionale Interfaces

� Single-Abstract-Method-Typen (SAM-Typen)

� Darf nur eine abstrakte Methode enthalten

� … aber beliebig viele Default-Methoden

� Annotation java.lang.FunctionalInterface

� Beispiele: Runnable, Comparator, ActionListener, …

Evaluation eines Lambda-Ausdrucks

� Funktionales Interface ist sog. Target Type für Lambda-Ausdruck

� Rückgabetyp der abstrakten Methode

� Parametertypen der abstrakten Methode

� Deklarierte Exceptions der abstrakten Methode

� Verbesserte Typinferenz von Java 8 bringt Lambda und Interface zusammen

31

Co

pyr

igh

201

4 A

ccso

Gm

bH

Ein Lambda-Ausdruck evaluiert zu einem funktionalen Interface.

Comparator<Integer> sortAscending = (Integer a, Integer b) -> a-b;

@FunctionalInterfacepublic interface Comparator<T>() {

int compare(T o1, T o2);...

};

Rückgabetyp stimmt überein

Parametertyp stimmt überein

32

Co

pyr

igh

201

4 A

ccso

Gm

bHAgenda: Was kommt in Java 8 mit JSR 335?

Lambda-Ausdrücke

Verbesserte Typinferenz

Methodenreferenzen

Collections & Streams

Filter/Map/Reduce, Parallelisierung

Funktionale InterfacesDefault-

Methoden

in

Interfacesjava.util.function

33

Co

pyr

igh

201

4 A

ccso

Gm

bH

Im Package java.util.function sind funktionale Interfaces, die für typische Use-Cases bestimmt sind.

Neue funktionale Interfaces

1. Function - Zur Abbildung von Funktionen

2. Consumer - Zur Abbildung von Prozeduren mit Seiteneffekten

3. Predicate - Zur Abbildung von Prädikaten

4. Supplier - Zur Abbildung von Objektfabriken

Neue Klasse Optional<T>

� Vereinfacht Null-Behandlung im Code

� Methoden können einfach Optional<T> liefern

� Optional bietet viele Convenience-Methoden an

� Arbeiten mit funktionalen Interfaces zusammen

@NotNull Optional<T> opt = ...opt.ifPresent(t -> doSomething(t));

34

Co

pyr

igh

201

4 A

ccso

Gm

bH

1) Das funktionale Interface Function<T,R> ist ein Transformator eines Eingabetyps T in einen Ausgabetyp R.

@FunctionalInterfacepublic interface Function<T, R> {

public R apply(T t);

public default <V> Function<T, V> compose(Function<? super R, ? extends V> after)

{Objects.requireNonNull(after);return (T t) -> after.apply(apply(t));

}}

Function<Integer, Integer> square = n -> n*n;assertEquals(9, square.apply(3));

Definition

Einfaches Beispiel

Definition

Evaluation

35

Co

pyr

igh

201

4 A

ccso

Gm

bH

1) Ein typisches Nutzungsszenario für solcheFunction<T,R> -Instanzen sind Transformatoren bei map()

public static <I, O> List<O> map(Function<I, O> mapper, List<I> input) {final List<O> outputList = new ArrayList<>();input.forEach(i -> {O o = mapper.apply(i);outputList.add(o);

});return outputList;

}

Code-as-Data� Nicht Function<T,R>.apply(arg) direkt aufrufen, …

� … sondern Function<T,R> als Lambda-Ausdruck übergeben!

map(x -> Integer.valueOf(x), input);

generische Funktion Lambda-Ausdruck Daten

36

Co

pyr

igh

201

4 A

ccso

Gm

bH

2) Ein Consumer<T> ist eine Funktion, die den Programmzustand durch Seiteneffekte verändert.

@FunctionalInterfacepublic interface Consumer<T> {

public void accept(T t);

public default Consumer<T> chain(Consumer<? super T> other)

{Objects.requireNonNull(other);return (T t) -> { accept(t); other.accept(t);};

}}

Definition

Eigenschaften� Analog zu Function<T,R>,

aber keine Rückgabe sondern Seiteneffekt

� Mehrere Consumer<T> kann man miteinander verketten -Ausführung von links nach rechts

Einfaches BeispielConsumer<String> out = s -> System.out.println(s);out.accept("Test");

37

Co

pyr

igh

201

4 A

ccso

Gm

bH

2) Ein typisches Nutzungsszenario für Consumer<T> -Instanzen ist die Verarbeitung von Collections mit forEach.

public static <T> void forEach(Consumer<T> consumer, List<T> input) {for (T t : input) {consumer.accept(t);

}}

Code-as-Data� Nicht Consumer<T>.accept(arg) direkt aufrufen, …

� … sondern Consumer<T> als Lambda-Ausdruck übergeben!

forEach(s -> System.out.println(s), input);

generische Funktion Lambda-Ausdruck Daten

38

Co

pyr

igh

201

4 A

ccso

Gm

bH

3) Ein Predicate<T> ist eine Funktion, die ein Objekt vom Typ T einem logischen Test unterzieht.

@FunctionalInterfacepublic interface Predicate<T> {

public boolean test(T t);

public default Predicate<T> and(...) { ... }public default Predicate<T> negate() { ... }public default Predicate<T> or(...) { ... }public default Predicate<T> xor(...) { ... }

}

Definition

39

Co

pyr

igh

201

4 A

ccso

Gm

bH

3) Ein typisches Nutzungsszenario für Predicate<T>-Instanzen ist die Filterung von Collections mit filter()

public static <T> List<T> filter(Predicate<T> criterion, List<T> input) {final List<T> filteredList = new ArrayList<>();input.forEach(i -> {if (criterion.test(i)) {filteredList.add(i);

}});return filteredList;

}

Code-as-Data� Nicht Predicate<T>.test(arg) direkt aufrufen, …

� … sondern Predicate<T> als Lambda-Ausdruck übergeben!

filter(n -> n % 2 == 0, input);

generische Funktion Lambda-Ausdruck Daten

40

Co

pyr

igh

201

4 A

ccso

Gm

bH

3) Predicate<T>-Instanzen kann man zu komplexeren Testkriterien verknüpfen. lc03_predicates

Image courtesy of phanlop88 / FreeDigitalPhotos.net

41

Co

pyr

igh

201

4 A

ccso

Gm

bH

3) Predicate<T>-Instanzen kann man zu komplexeren Testkriterien verknüpfen.

Einfaches BeispielPredicate<Integer> isEven = n -> n % 2 == 0;assertTrue(isEven.test(2));

Predicate<Integer> isOdd = isEven.negate();assertTrue(isOdd.test(3));

Negation

Und-VerknüpfungPredicate<Integer> isPositive = n -> n > 0;Predicate<Integer> isEvenAndPositive =

isEven.and(isPositive);assertTrue(isEvenAndPositive.test(2));assertFalse(isEvenAndPositive.test(0));

Oder-VerknüpfungPredicate<Integer> isZero = n -> n == 0;Predicate<Integer> isZeroOrPositive =

isPositive.or(isZero);assertTrue(isZeroOrPositive.test(0));

42

Co

pyr

igh

201

4 A

ccso

Gm

bHEnde

Image courtesy of phanlop88 / FreeDigitalPhotos.net

43

Co

pyr

igh

201

4 A

ccso

Gm

bH

4) Ein Supplier<T> ist eine Factory-Funktion, die Objekte vom Typ T liefert.

@FunctionalInterfacepublic interface Supplier<T> {public T get();

}

Definition

Einfaches BeispielSupplier<Integer> generator =

() -> (int) (Math.random() * NUMBER_RANGE);int randomNumber = generator.get();assertTrue(

randomNumber > 0 && randomNumber <= NUMBER_RANGE);

Nutzung?� Zufallszahlengeneratoren braucht man jetzt nicht so oft …

� Am ehesten als …

� Closure um Collection, aus der Elemente geholt werden

� Objektfabrik

44

Co

pyr

igh

201

4 A

ccso

Gm

bH

java.util.function enthält weitere funktionale Interfaces - im Wesentlichen syntaktischer Zucker…

Binäre

FunktionenBiConsumer

BiFunction

BiPredicate

Typ-

parametrierte

Funktionen

BinaryOperator

IntConsumer

DoubleFunction

LongFunction

LongBinary-

Operator

DoubleBinary-

Operator

@FunctionalInterfacepublic interface BinaryOperator<T> extends BiFunction<T,T,T> {}

45

Co

pyr

igh

201

4 A

ccso

Gm

bHAgenda: Was kommt in Java 8 mit JSR 335?

Lambda-Ausdrücke

Verbesserte Typinferenz

Methodenreferenzen

Collections & Streams

Filter/Map/Reduce, Parallelisierung

Funktionale InterfacesDefault-

Methoden

in

Interfacesjava.util.function

46

Co

pyr

igh

201

4 A

ccso

Gm

bH

Das Design der Streams-API basiert auf einer Pipes-and-Filters-Architektur.

ps –ef | grep login | cut –c 50- | head

Decrypt Authenticate De-DupPipePipe Pipe Pipe

Filter Filter FilterIncoming

Order

Clean

Order

Verkettung von Unix-Programmen

Enterprise Integration Patterns

47

Co

pyr

igh

201

4 A

ccso

Gm

bHInterne Iteration, Streams, Filter/Map/Reduce

lc04_streams

Image courtesy of phanlop88 / FreeDigitalPhotos.net

48

Co

pyr

igh

201

4 A

ccso

Gm

bH

Externe Iteratoren sind einfach zu benutzen, dafür aber inhärent sequentiell und nicht zusammensetzbar.

List<Integer> prices = Arrays.asList(10, 20, 30, 40, 50);double totalCost = 0.0;for (Integer price : prices) {totalCost += price;

}return totalCost;

List<Integer> prices = Arrays.asList(10, 20, 30, 40, 50);double totalCost = 0.0;for (Integer price : prices) {totalCost += price * 0.9;

}return totalCost;

List<Integer> prices = Arrays.asList(10, 20, 30, 40, 50);double totalCost = 0.0;for (Integer price : prices) {if (price >= 40)totalCost += price * 0.9;

}return totalCost;

List<Integer> prices = Arrays.asList(10, 20, 30, 40, 50);double totalCost = 0.0;for (Integer price : prices) {if (price >= 40)totalCost += price * 0.9;

}return totalCost;

Eigenschaften� Nicht parallelisierbar

� Nicht zusammensetzbar

� Iteration über Preise

� Filterung nach bestimmten Preisen

� Reduktion um 10% pro Preis

� Summierung der Teilergebnisse

� Reduktion und Summierung auch noch vermischt

49

Co

pyr

igh

201

4 A

ccso

Gm

bH

Die Steuerung einer internen Iteration obliegt dem Container. Ein interner Iterator ist zusammensetzbar.

List<Integer> prices = Arrays.asList(10, 20, 30, 40, 50);return prices.stream().sum();

List<Integer> prices = Arrays.asList(10, 20, 30, 40, 50);return prices.stream().mapToDouble(price -> price * 0.9).sum();

List<Integer> prices = Arrays.asList(10, 20, 30, 40, 50);return prices.stream().filter(price -> price >= 40).mapToDouble(price -> price * 0.9).sum();

List<Integer> prices = Arrays.asList(10, 20, 30, 40, 50);return prices.stream().filter(price -> price >= 40).mapToDouble(price -> price * 0.9).sum();

Eigenschaften� Lambda-Funktion kodiert Verhalten …

� … wird an generische Methode des Containers übergeben

� Container entscheidet, ob sequentiell oder parallel und / oder lazy

� Dekomposition in einzelne, funktionale Aspekte

� Schritt 1, Schritt 2, …, Schritt k

� „Fluss“ ist sofort erkennbar

50

Co

pyr

igh

201

4 A

ccso

Gm

bHEnde

Image courtesy of phanlop88 / FreeDigitalPhotos.net

51

Co

pyr

igh

201

4 A

ccso

Gm

bH

Das Design der Streams-API basiert auf einer Pipes-and-Filters-Architektur.

prices.stream().filter(p -> p >= 40).mapToDouble(p -> p * 0.9).sum();

Source

52

Co

pyr

igh

201

4 A

ccso

Gm

bH

Das Design der Streams-API basiert auf einer Pipes-and-Filters-Architektur.

prices.stream().filter(p -> p >= 40).mapToDouble(p -> p * 0.9).sum();

Source IntermediateOp

Stream<Integer>

53

Co

pyr

igh

201

4 A

ccso

Gm

bH

Das Design der Streams-API basiert auf einer Pipes-and-Filters-Architektur.

prices.stream().filter(p -> p >= 40).mapToDouble(p -> p * 0.9).sum();

Source IntermediateOp

Stream<Integer>

IntermediateOp

Stream<Integer>

54

Co

pyr

igh

201

4 A

ccso

Gm

bH

Das Design der Streams-API basiert auf einer Pipes-and-Filters-Architektur.

prices.stream().filter(p -> p >= 40).mapToDouble(p -> p * 0.9).sum();

Source IntermediateOp

Stream<Integer>

IntermediateOp

Stream<Integer>

TerminalOp

Stream<Double> Double

55

Co

pyr

igh

201

4 A

ccso

Gm

bH

Intermediäre und terminale Operationen sind Wrapper um einen Lambda-Ausdruck.

prices.stream().filter(p -> p >= 40).mapToDouble(p -> p * 0.9).sum();

Source FilterOp

Stream<Integer>

IntermediateOp

Stream<Integer>

TerminalOp

Stream<Double> Double

Instanz von Predicate<Integer>

56

Co

pyr

igh

201

4 A

ccso

Gm

bH

Intermediäre und terminale Operationen sind Wrapper um einen Lambda-Ausdruck.

prices.stream().filter(p -> p >= 40).mapToDouble(p -> p * 0.9).sum();

Source FilterOp

Stream<Integer>

MapOp

Stream<Integer>

TerminalOp

Stream<Double> Double

Instanz von Predicate<Integer>

Instanz von Instanz von ToDoubleFunction

<Integer>

57

Co

pyr

igh

201

4 A

ccso

Gm

bH

Intermediäre und terminale Operationen sind Wrapper um einen Lambda-Ausdruck.

prices.stream().filter(p -> p >= 40).mapToDouble(p -> p * 0.9).sum();

Source FilterOp

Stream<Integer>

MapOp

Stream<Integer>

ReduceOp

Stream<Double> Double

Instanz von Predicate<Integer>

Instanz von Instanz von ToDoubleFunction

<Integer>

Instanz von DoubleBinaryOperator

58

Co

pyr

igh

201

4 A

ccso

Gm

bHDie Auswertung der Operationen erfolgt lazy bzw. eager.

prices.stream().filter(p -> p >= 40).mapToDouble(p -> p * 0.9).sum();

Source FilterOp

Stream<Integer>

MapOp

Stream<Integer>

ReduceOp

Stream<Double> Double

Instanz von Predicate<Integer>

Instanz von Instanz von ToDoubleFunction

<Integer>

Instanz von DoubleBinaryOperator

lazy lazy eager

59

Co

pyr

igh

201

4 A

ccso

Gm

bH

Die Auswertung von intermed. Operationen erfolgt erst, wenn der Stream durch eine terminale Operation angestossen wird.

String[] txt = { "State", "of", "the", "Lambda", "Libraries", "Edition" };IntStream is =

Arrays.stream(txt).filter(s -> s.length() > 3).mapToInt(s -> { System.out.println(s + ","); return s.length(); });

// erste terminale Operationis.forEach(l -> System.out.print(l + ", "));

Mit der Variable is halten und nutzen wir eine Referenz auf den Stream. Das ist schlechter Stil und potentiell gefährlich, denn zwei oder mehr terminale Operationen könnten darauf aufgerufen werden, was nicht erlaubt ist.

Nur eine terminale Operation ist möglich! Danach ist der Stream “verbraucht”.

60

Co

pyr

igh

201

4 A

ccso

Gm

bH

String[] txt = { "State", "of", "the", "Lambda", "Libraries", "Edition" };IntStream is =

Arrays.stream(txt).filter(s -> s.length() > 3).mapToInt(s -> { System.out.println(s + ","); return s.length(); });

// erste terminale Operationis.forEach(l -> System.out.print(l + ", "));

// zweite terminale Operationint sum = is.reduce(0, (l1, l2) -> { return (l1 + l2); });

Laufzeitfehler daher bei der zweiten terminalen Operation!

Exception in thread "main" java.lang.IllegalStateEx ception: stream has already been operated uponat java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:221)at java.util.stream.IntPipeline.reduce(IntPipeline.java:453)...

Die Auswertung von intermed. Operationen erfolgt erst, wenn der Stream durch eine terminale Operation angestossen wird.

61

Co

pyr

igh

201

4 A

ccso

Gm

bH

Die Streams-API bietet eine Fülle generischer Methoden, die wir zur Verarbeitung von Collections nutzen können.

Operation Rückgabe Evaluation Interface λ-Signatur

filter Stream<T> lazy Predicate<T> T � boolean

map Stream<R> lazy Function<T, R> T � R

reduce T eager BinaryOperator<T> (T, T) � T

limit Stream<T> lazy - -

findFirst Optional<T> eager - -

forEach void eager Block<T> T � void

sorted Stream<T> lazy Comparator<T> (T, T) � boolean

collect Collection<R> eager - -

anyMatch boolean eager Predicate<T> T � boolean

peek Stream<T> lazy - -

... ... ... ... ...

62

Co

pyr

igh

201

4 A

ccso

Gm

bH

Die Streams-API bietet eine Fülle generischer Methoden, die wir zur Verarbeitung von Collections nutzen können.

Operation Rückgabe Evaluation Interface λ-Signatur

filter Stream<T> lazy Predicate<T> T � boolean

map Stream<R> lazy Function<T, R> T � R

reduce T eager BinaryOperator<T> (T, T) � T

limit Stream<T> lazy - -

findFirst Optional<T> eager - -

forEach void eager Block<T> T � void

sorted Stream<T> lazy Comparator<T> (T, T) � boolean

collect Collection<R> eager - -

anyMatch boolean eager Predicate<T> T � boolean

peek Stream<T> lazy - -

... ... ... ... ...

peek() ist nützlich zum Debuggen. Wie geht das sonst?!?!?...List<Integer> prices = Arrays.asList(10, 20, 30,40, 50, 60, 70); return prices.stream()

.peek(price -> System.out.println(price))

.filter(price -> price >= 40) // Filter

.peek(price -> System.out.println(price))

.mapToDouble(price -> price * 0.9) // Map: Rabatt

.peek(price -> System.out.println(price))

.sum ();

63

Co

pyr

igh

201

4 A

ccso

Gm

bHAgenda: Was kommt in Java 8 mit JSR 335?

Lambda-Ausdrücke

Verbesserte Typinferenz

Methodenreferenzen

Collections & Streams

Fehlerbehandlung

Funktionale InterfacesDefault-

Methoden

in

Interfacesjava.util.function

64

Co

pyr

igh

201

4 A

ccso

Gm

bHFehlerbehandlung in Streams

lc05_streams_errorhandling

Image courtesy of phanlop88 / FreeDigitalPhotos.net

65

Co

pyr

igh

201

4 A

ccso

Gm

bH

Error Handling in Streams (1/3)ohne Exception-Handling in try/catch

String[] txt = { "abc", "abcde", "abc" };

// Stream wird aufgebautStream<Character> s1 = Arrays.stream(txt).

map(t -> {// gezielte Exception fuer alle Strings mit Laenge > 3if (t.length()>3) throw new RuntimeException("test too long");return t.charAt(0);

});

// Stream wird konsumierts1.forEach( c -> System.out.print(c.charValue() + ",") );

Ausgabe: a, danach Exception (da abcde zu lange ist).

abc wird nicht mehr verarbeitet.

66

Co

pyr

igh

201

4 A

ccso

Gm

bH

Error Handling in Streams (2/3)Exception-Handling in der intermediären Operation

String[] txt = { "rst", "rstuv", "rst" };

// Stream wird aufgebautStream<Character> s2 = Arrays.stream(txt).

map(t -> {try {

if (t.length()>3) throw new RuntimeException("test too long");

return t.charAt(0);}catch (Exception ex) {

return null;}

});

// Stream wird konsumierts2.forEach(c -> { if (c!=null) System.out.print(c.charValue() + ","); });

Ausgabe: r und r

67

Co

pyr

igh

201

4 A

ccso

Gm

bH

Error Handling in Streams (3/3)Exception-Handling in der terminalen Operation

String[] txt = { "xyz", "vwxyz", "xyz" };

// Stream wird aufgebautStream<Character> s3 = Arrays.stream(txt).

map(t -> {if (t.length()>3) throw new RuntimeException("test too long");return t.charAt(0);

});

// Stream wird konsumierts3.forEach( c -> {

try {System.out.print(c.charValue() + ",");

}catch (Exception ex) {

ex.printStackTrace(System.err);}

} );

Ausgabe: x, danach Exception (da vwxyz zu lange ist).

xyz wird nicht mehr verarbeitet.

68

Co

pyr

igh

201

4 A

ccso

Gm

bHEnde

Image courtesy of phanlop88 / FreeDigitalPhotos.net

69

Co

pyr

igh

201

4 A

ccso

Gm

bHAgenda: Was kommt in Java 8 mit JSR 335?

Lambda-Ausdrücke

Verbesserte Typinferenz

Methodenreferenzen

Collections & Streams

Parallelisierung

Funktionale InterfacesDefault-

Methoden

in

Interfacesjava.util.function

70

Co

pyr

igh

201

4 A

ccso

Gm

bHWie kann man Programme überhaupt parallelisieren?

Schritt 1

Schritt 2

Schritt 3

Schritt 4

Schritt 5

Schritt 6

Variante 1: Parallelisierung des Datenflussesfo

r 1

... k

do

Schritt 1

Schritt 2

Schritt 3

Schritt 4

Schritt 5

Schritt 6

for

each

ele

men

t d

o in

par

alle

l Schritt 1

Schritt 2

Schritt 3

Schritt 4

Schritt 5

Schritt 6

Schritt 1

Schritt 2

Schritt 3

Schritt 4

Schritt 5

Schritt 6

Schritt 1

Schritt 2

Schritt 3

Schritt 4

Schritt 5

Schritt 6

......

71

Co

pyr

igh

201

4 A

ccso

Gm

bHWie kann man Programme überhaupt parallelisieren?

Schritt 1

Schritt 2

Schritt 3

Schritt 4

Schritt 5

Schritt 6

Schritt 1

Schritt 2 Schritt 3

4a 4b 4c

Schritt 5

Schritt 6

Variante 2: Parallelisierung des Kontrollflusses

72

Co

pyr

igh

201

4 A

ccso

Gm

bHWie kann man Programme überhaupt parallelisieren?

Schritt 1

Schritt 2

Schritt 3

Schritt 4

Schritt 5

Schritt 6

Schritt 1

Schritte 2-5

Schritt 6

2-5.1 2-5.2 2-5.n

2-5.1.1 2-5.1.2 2-5.1.n

Variante 2b: rekursive Parallelisierung des Kontrollflusses

Fork/Join mit Teile&Herrsche!

73

Co

pyr

igh

201

4 A

ccso

Gm

bHFork-Join adressiert leicht zu parallelisierende Probleme

Teile und Herrsche

� Zerlege Problem sukzessive in Teilprobleme

� ... bis Teilproblem klein genug, so dass es direkt gelöst werden kann

� ... und führe dann die Ergebnisse zusammen

� „embarassingly parallel“

// join

loeseProblemMitForkJoin (Problem p)

if (p ist klein genug)

loese p direkt und sequentiell

else

teile p in unabhängige Teilprobleme p1 und p2 / / split

loese p1 und p2 unabhängig voneinander (rekursiv) / / fork

führe Teilergebnisse von p1 und p2 zusammen // join

74

Co

pyr

igh

201

4 A

ccso

Gm

bH

Fork-Join-Baum der Aufgaben. Basis der Parallelisierung ist Fork/Join-Framework aus Java7 (JSR166y).

T

rekursive Aufteilung in

Teilprobleme in der Fork-Phase

Zusammenführung der

Teilergebnisse in der Join-Phase

Problem klein

genug?

Sequentielle

Berechnung

T1

T2

T1

T2

T1

T2

T1

T2

TT

t

T11

T21

T22

T12

T11

T21

T22

T12

75

Co

pyr

igh

201

4 A

ccso

Gm

bH

Sequentielle und parallele Streams:Basis der Parallelisierung ist Fork/Join aus Java7 (JSR166y)

String[] txt = { "State", "of", "the", "Lambda", "Libraries", "Edition" };IntStream is =

Arrays.stream(txt).filter(s -> s.length() > 3).mapToInt(s -> s.length());

int sum = is.reduce(0, (l1, l2) -> { return (l1 + l2); });

String[] txt = { "State", "of", "the", "Lambda", "Libraries", "Edition" };IntStream is =

Arrays.stream(txt).parallel(). // auch: Collection.parallelStream()filter(s -> s.length() > 3).mapToInt(s -> s.length());

int sum = is.reduce(0, (l1, l2) -> { return (l1 + l2); });

76

Grenzen der Parallelisierung: Unterscheide zustandslose von zustandsbehafteten intermediären Operationen

Zustandslose intermediäreOperationen

� Zustandslose intermediäre Operationen bearbeiten ein einzelnes Element

� Beispiele

� filter

� map

� Problemlos parallelisierbar, keine Synchronisierung erforderlich

� Zustandsbehaftete intermediäre Operationen benötigen einen zusätzlichen Kontext

� Beispiele

� limit nur die ersten k Elemente

� distinct nur disjunkte Elemente

� sorted Sortierung, siehe Folgefolie

� Schwierig parallelisierbar

Zustandsbehaftete intermediäreOperationen

77

Was passiert in einem ParallelStream bei sorted()?

...stream().parallel() .statelessOps() .sorted() .statelessOps() .terminal()

Barriere: Parallelisierung

wird hier gezielt

sequentialisiert.

Ergebnisse werden

zwischengepuffert (auch

bei seq. Stream!)

Parallelisierung

wird neu aufgesetzt

Sequentielle

Bearbeitung

(in einem Thread).

78

Beispiel „Finde das maximale Elemente mit einem Parallel-Stream“: Vorsicht beim Performance-Vergleich!

T

T1

T2

T1

T2

T1

T2

T1

T2

TT

T11

T21

T22

T12

[1, n]

[1,�

�]

[�

�+ 1, n]

max(T11,T12)

max(T21,T22)

max(T1,T2)

[1,�

�]

[�

�+ 1,

�]

[�

�+ 1,

��

�]

[��

�+ 1, n]

int[] ints = { 17, 453, 5, 1, 458, 48, 6, 99, /* ... etc. ... */ };int max = Arrays.stream(ints).parallel().

reduce(Integer.MIN_VALUE, (i,j) -> Math.max(i,j));System.out.println(max);

79

Performance bei der Parallelisierung in Streams: Old Style vs. Seq. Streams vs. Parallele Streams

Image courtesy of phanlop88 / FreeDigitalPhotos.net

lc06_streams_parallel

80

int[] ints = new int[64] ;for (int i=0; i<64; i++) ints[i] = i;

List<String> stringList= new LinkedList<>();

Arrays.stream(ints).parallel().mapToObj(String::valueOf)

.forEach(stringList::add);

System.out.println(stringList);

int[] ints = new int[64] ;for (int i=0; i<64; i++) ints[i] = i;

List<String> stringList= new LinkedList<>();

Arrays.stream(ints).parallel().mapToObj(String::valueOf).sequential().forEach(stringList::add);

System.out.println(stringList);

Sequentieller/Paralleler Moduswechsel und -Check im Stream selbst möglich!

Methoden� Stream.isParallel() Check, ob Stream parallel ist

� Stream.parallel() Modifikation des Streams ...

� Stream.sequential() ... zu par./seq. Stream möglich

int[] ints = new int[64] ;for (int i=0; i<64; i++) ints[i] = i;

List<String> stringList = new LinkedList<>();

Arrays.stream(ints) .mapToObj(String::valueOf).forEach(stringList::add);

System.out.println(stringList);

Achtung: Nicht thread-safe!

81

Co

pyr

igh

201

4 A

ccso

Gm

bH

Literatur zum Thema von uns bei JavaSPEKTRUM und Heise Developer… und die heutigen Folien auf accso.de

Markus Günther, Martin Lehmann„Lambda-Ausdrücke in Java 8“.

In JavaSPEKTRUM, 03/2013PDF-Download hier:http://www.sigs-datacom.de/fachzeitschriften/javaspektrum/archiv/artikelansicht.html?tx_mwjournals_pi1[pointer]=0&tx_mwjournals_pi1[mode]=1&tx_mwjournals_pi1[showUid]=7237

Markus Günther, Martin Lehmann„Java 7: Das Fork-Join-Framework für mehr Performance“

In JavaSPEKTRUM, 05/2012PDF-Download hier:

http://www.sigs-datacom.de/fachzeitschriften/javaspektrum/archiv/artikelansicht.html?tx_mwjournals_pi1[pointer]=0&tx_mwjournals_pi1[mode]=1&tx_mwjournals_pi1[showUid]=7396

Markus Günther, Martin Lehmann„Streams und Collections in Java 8“.

Heise Developer, http://www.heise.de/developer/artikel/Streams-und-

Collections-in-Java-8-2151376.html

82

Co

pyr

igh

201

4 A

ccso

Gm

bH

Weitere Links und Literatur zu Java 8

https://jdk8.java.net/

http://www.techempower.com/blog/2013/03/26/everything-about-java-8/

http://www.heise.de/developer/artikel/Was-Entwickler-mit-Java-8-erwartet-1932997.html

Weitere Links

Begeisterung für die

anspruchsvollen Aufgaben unserer Kunden

Individuelle

Kernsysteme

Beschleunigte

SoftwaretechnikTeam≡