Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Ausdrucksstark: Ein vertiefter Blick auf die neuen Lambda-Ausdrücke in Java 8 (JSR 335)

Ausdrucksstark: Ein vertiefter Blick auf die neuen Lambda-Ausdrücke in Java 8 (JSR 335)

Martin Lehmann, Markus Günther: „Ausdrucksstark: Ein vertiefter Blick auf die neuen Lambda-Ausdrücke in Java 8 (JSR 335)“.
Java User Group Frankfurt, 26. März 2014

https://sites.google.com/site/jugffm/home/26-03-2014-java8-lambdas-streams-jsr33

Martin Lehmann

March 26, 2014
Tweet

More Decks by Martin Lehmann

Other Decks in Programming

Transcript

  1. 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
  2. 2 Copyright © 2014 Accso GmbH About 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-Dienstleister Accso – Accelerated Solutions GmbH in Darmstadt www.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. 3 Copyright © 2014 Accso GmbH Der 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. 4 Copyright © 2014 Accso GmbH Agenda: 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
  5. 5 Copyright © 2014 Accso GmbH Agenda: 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. 6 Copyright © 2014 Accso GmbH 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 1 public static void main(...) { new FooImpl().foo(); } } public interface Fooable { void foo(); // Version 1 }
  7. 7 Copyright © 2014 Accso GmbH Was passiert, wenn ich

    einem Interface eine Methode hinzufügen möchte? public interface Fooable { void foo(); // Version 2 void bar(); } class App { // Version 2 public static void main(...) { new FooImpl().bar(); } } public class FooImpl implements Fooable { void foo() { System.out.println(“Hello!”); } } class App { // Version 1 public static void main(...) { new FooImpl().foo(); } } public interface Fooable { void foo(); // Version 1 } java.lang. NoSuchMethodError: Fooable.bar()V
  8. 8 Copyright © 2014 Accso GmbH 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-Methods in 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. 9 Copyright © 2014 Accso GmbH Zur Compile-Zeit muss eindeutig

    sein, welche Default- Methode zur Link-Zeit in Implementierungen genutzt wird. Regel 1 Class 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 2 Subtype 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. 10 Copyright © 2014 Accso GmbH 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. 11 Copyright © 2014 Accso GmbH 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. 12 Copyright © 2014 Accso GmbH Konflikte 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(); } @Override public void sayHello() { D.super.sayHello(); } } C2 kompiliert nun wg. expliziter Auflösung
  13. 13 Copyright © 2014 Accso GmbH Ist 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. 14 Copyright © 2014 Accso GmbH Default-Methoden: Mehrfachvererbung kann zu

    überraschenden Ergebnissen führen. lc01_interfaces_defaultmethods Image courtesy of phanlop88 / FreeDigitalPhotos.net
  15. 15 Copyright © 2014 Accso GmbH 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. 16 Copyright © 2014 Accso GmbH Ende Image courtesy of

    phanlop88 / FreeDigitalPhotos.net
  17. 17 Copyright © 2014 Accso GmbH Agenda: 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
  18. 18 Copyright © 2014 Accso GmbH Lambda-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. 19 Copyright © 2014 Accso GmbH Lambda-Ausdrücke in Java 8

    Ohne Typangabe x -> 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. 20 Copyright © 2014 Accso GmbH Wozu 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 Funktionaler Programmierstil Höherer Abstraktionsgrad durch Funktionen höherer Ordnung Eleganter, kürzer, prägnanter Keine Seiteneffekte
  21. 21 Copyright © 2014 Accso GmbH Lambda-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. 22 Copyright © 2014 Accso GmbH Von Anonymous Inner Classes

    zu Lambdas lc02_anoninnerclass Image courtesy of phanlop88 / FreeDigitalPhotos.net
  23. 23 Copyright © 2014 Accso GmbH Anonyme innere Klassen lassen

    sich einfach in äquivalente Lambda-Ausdrücke migrieren. Comparator<Integer> sortAscending = new Comparator<Integer>() { @Override public int compare(Integer a, Integer b) { return a-b; } }; Typischer Code für einen Comparator, der Ganzzahlen vergleicht … Comparator<Integer> sortAscending = new Comparator<Integer>() { @Override public 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. 24 Copyright © 2014 Accso GmbH Anonyme innere Klassen lassen

    sich einfach in äquivalente Lambda-Ausdrücke migrieren. Comparator<Integer> sortAscending = new Comparator<Integer>() { @Override public 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>() { @Override public int compare(Integer a, Integer b) { return a-b; } };
  25. 25 Copyright © 2014 Accso GmbH 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. 26 Copyright © 2014 Accso GmbH Ende Image courtesy of

    phanlop88 / FreeDigitalPhotos.net
  27. 27 Copyright © 2014 Accso GmbH Performance? 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. 28 Copyright © 2014 Accso GmbH Performance? 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 Aufsetzen der capture Konstruktion Konstruktor aufrufen Erzeugung des Lambda-Ausdrucks Aufruf invokeinterface invokedynamic
  29. 29 Copyright © 2014 Accso GmbH Agenda: 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
  30. 30 Copyright © 2014 Accso GmbH 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. 31 Copyright © 2014 Accso GmbH Ein Lambda-Ausdruck evaluiert zu

    einem funktionalen Interface. Comparator<Integer> sortAscending = (Integer a, Integer b) -> a-b; @FunctionalInterface public interface Comparator<T>() { int compare(T o1, T o2); ... }; Rückgabetyp stimmt überein Parametertyp stimmt überein
  32. 32 Copyright © 2014 Accso GmbH Agenda: 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
  33. 33 Copyright © 2014 Accso GmbH 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. 34 Copyright © 2014 Accso GmbH 1) Das funktionale Interface

    Function<T,R> ist ein Transformator eines Eingabetyps T in einen Ausgabetyp R. @FunctionalInterface public 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. 35 Copyright © 2014 Accso GmbH 1) Ein typisches Nutzungsszenario

    für solche Function<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. 36 Copyright © 2014 Accso GmbH 2) Ein Consumer<T> ist

    eine Funktion, die den Programmzustand durch Seiteneffekte verändert. @FunctionalInterface public 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 Beispiel Consumer<String> out = s -> System.out.println(s); out.accept("Test");
  37. 37 Copyright © 2014 Accso GmbH 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. 38 Copyright © 2014 Accso GmbH 3) Ein Predicate<T> ist

    eine Funktion, die ein Objekt vom Typ T einem logischen Test unterzieht. @FunctionalInterface public 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. 39 Copyright © 2014 Accso GmbH 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. 40 Copyright © 2014 Accso GmbH 3) Predicate<T>-Instanzen kann man

    zu komplexeren Testkriterien verknüpfen. lc03_predicates Image courtesy of phanlop88 / FreeDigitalPhotos.net
  41. 41 Copyright © 2014 Accso GmbH 3) Predicate<T>-Instanzen kann man

    zu komplexeren Testkriterien verknüpfen. Einfaches Beispiel Predicate<Integer> isEven = n -> n % 2 == 0; assertTrue(isEven.test(2)); Predicate<Integer> isOdd = isEven.negate(); assertTrue(isOdd.test(3)); Negation Und-Verknüpfung Predicate<Integer> isPositive = n -> n > 0; Predicate<Integer> isEvenAndPositive = isEven.and(isPositive); assertTrue(isEvenAndPositive.test(2)); assertFalse(isEvenAndPositive.test(0)); Oder-Verknüpfung Predicate<Integer> isZero = n -> n == 0; Predicate<Integer> isZeroOrPositive = isPositive.or(isZero); assertTrue(isZeroOrPositive.test(0));
  42. 42 Copyright © 2014 Accso GmbH Ende Image courtesy of

    phanlop88 / FreeDigitalPhotos.net
  43. 43 Copyright © 2014 Accso GmbH 4) Ein Supplier<T> ist

    eine Factory-Funktion, die Objekte vom Typ T liefert. @FunctionalInterface public interface Supplier<T> { public T get(); } Definition Einfaches Beispiel Supplier<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. 44 Copyright © 2014 Accso GmbH java.util.function enthält weitere funktionale

    Interfaces - im Wesentlichen syntaktischer Zucker… Binäre Funktionen BiConsumer BiFunction BiPredicate Typ- parametrierte Funktionen BinaryOperator … IntConsumer DoubleFunction LongFunction … LongBinary- Operator DoubleBinary- Operator … @FunctionalInterface public interface BinaryOperator<T> extends BiFunction<T,T,T> { }
  45. 45 Copyright © 2014 Accso GmbH Agenda: 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
  46. 46 Copyright © 2014 Accso GmbH Das Design der Streams-API

    basiert auf einer Pipes-and-Filters-Architektur. ps –ef | grep login | cut –c 50- | head Decrypt Authenticate De-Dup Pipe Pipe Pipe Pipe Filter Filter Filter Incoming Order Clean Order Verkettung von Unix-Programmen Enterprise Integration Patterns
  47. 47 Copyright © 2014 Accso GmbH Interne Iteration, Streams, Filter/Map/Reduce

    lc04_streams Image courtesy of phanlop88 / FreeDigitalPhotos.net
  48. 48 Copyright © 2014 Accso GmbH 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. 49 Copyright © 2014 Accso GmbH 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. 50 Copyright © 2014 Accso GmbH Ende Image courtesy of

    phanlop88 / FreeDigitalPhotos.net
  51. 51 Copyright © 2014 Accso GmbH 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. 52 Copyright © 2014 Accso GmbH 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. 53 Copyright © 2014 Accso GmbH 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. 54 Copyright © 2014 Accso GmbH 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. 55 Copyright © 2014 Accso GmbH 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. 56 Copyright © 2014 Accso GmbH 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. 57 Copyright © 2014 Accso GmbH 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. 58 Copyright © 2014 Accso GmbH Die 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. 59 Copyright © 2014 Accso GmbH 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 Operation is.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. 60 Copyright © 2014 Accso GmbH 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 Operation is.forEach(l -> System.out.print(l + ", ")); // zweite terminale Operation int sum = is.reduce(0, (l1, l2) -> { return (l1 + l2); }); Laufzeitfehler daher bei der zweiten terminalen Operation! Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon at 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. 61 Copyright © 2014 Accso GmbH 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. 62 Copyright © 2014 Accso GmbH 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. 63 Copyright © 2014 Accso GmbH Agenda: Was kommt in

    Java 8 mit JSR 335? Lambda-Ausdrücke Verbesserte Typinferenz Methodenreferenzen Collections & Streams Fehlerbehandlung Funktionale Interfaces Default- Methoden in Interfaces java.util.function
  64. 65 Copyright © 2014 Accso GmbH Error Handling in Streams

    (1/3) ohne Exception-Handling in try/catch String[] txt = { "abc", "abcde", "abc" }; // Stream wird aufgebaut Stream<Character> s1 = Arrays.stream(txt). map(t -> { // gezielte Exception fuer alle Strings mit Laenge > 3 if (t.length()>3) throw new RuntimeException("test too long"); return t.charAt(0); }); // Stream wird konsumiert s1.forEach( c -> System.out.print(c.charValue() + ",") ); Ausgabe: a, danach Exception (da abcde zu lange ist). abc wird nicht mehr verarbeitet.
  65. 66 Copyright © 2014 Accso GmbH Error Handling in Streams

    (2/3) Exception-Handling in der intermediären Operation String[] txt = { "rst", "rstuv", "rst" }; // Stream wird aufgebaut Stream<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 konsumiert s2.forEach(c -> { if (c!=null) System.out.print(c.charValue() + ","); }); Ausgabe: r und r
  66. 67 Copyright © 2014 Accso GmbH Error Handling in Streams

    (3/3) Exception-Handling in der terminalen Operation String[] txt = { "xyz", "vwxyz", "xyz" }; // Stream wird aufgebaut Stream<Character> s3 = Arrays.stream(txt). map(t -> { if (t.length()>3) throw new RuntimeException("test too long"); return t.charAt(0); }); // Stream wird konsumiert s3.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.
  67. 68 Copyright © 2014 Accso GmbH Ende Image courtesy of

    phanlop88 / FreeDigitalPhotos.net
  68. 69 Copyright © 2014 Accso GmbH Agenda: Was kommt in

    Java 8 mit JSR 335? Lambda-Ausdrücke Verbesserte Typinferenz Methodenreferenzen Collections & Streams Parallelisierung Funktionale Interfaces Default- Methoden in Interfaces java.util.function
  69. 70 Copyright © 2014 Accso GmbH Wie kann man Programme

    überhaupt parallelisieren? Schritt 1 Schritt 2 Schritt 3 Schritt 4 Schritt 5 Schritt 6 Variante 1: Parallelisierung des Datenflusses for 1 ... k do Schritt 1 Schritt 2 Schritt 3 Schritt 4 Schritt 5 Schritt 6 for each element do in parallel 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 ... ...
  70. 71 Copyright © 2014 Accso GmbH Wie 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
  71. 72 Copyright © 2014 Accso GmbH Wie 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!
  72. 73 Copyright © 2014 Accso GmbH Fork-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
  73. 74 Copyright © 2014 Accso GmbH 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 T T t T11 T21 T22 T12 T11 T21 T22 T12
  74. 75 Copyright © 2014 Accso GmbH 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); });
  75. 76 Grenzen der Parallelisierung: Unterscheide zustandslose von zustandsbehafteten intermediären Operationen

    Zustandslose intermediäre Operationen 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äre Operationen
  76. 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).
  77. 78 Beispiel „Finde das maximale Elemente mit einem Parallel- Stream“:

    Vorsicht beim Performance-Vergleich! T T1 T2 T1 T2 T1 T2 T1 T2 T T 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);
  78. 79 Performance bei der Parallelisierung in Streams: Old Style vs.

    Seq. Streams vs. Parallele Streams Image courtesy of phanlop88 / FreeDigitalPhotos.net lc06_streams_parallel
  79. 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!
  80. 81 Copyright © 2014 Accso GmbH 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/2013 PDF-Download hier: http://www.sigs- datacom.de/fachzeitschriften/javaspektrum/archiv/artikelansicht.html?tx_mwjournals_pi 1[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/2012 PDF-Download hier: http://www.sigs- datacom.de/fachzeitschriften/javaspektrum/archiv/artikelansicht.html?tx_mwjournals_pi 1[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
  81. 82 Copyright © 2014 Accso GmbH 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