Slide 1

Slide 1 text

JAVA8 Such Streams… Much functional

Slide 2

Slide 2 text

NEWFEATURES • Lambdas • Streams API • java.util.Optional • Improved APIs for Collections @bsideup

Slide 3

Slide 3 text

LAMBDAS @bsideup Replace this… executor.submit(new Callable() { @Override public String call() throws Exception { return "Hello!"; } });

Slide 4

Slide 4 text

LAMBDAS @bsideup Replace this… executor.submit(new Callable() { @Override public String call() throws Exception { return "Hello!"; } }); With this! executor.submit(() -> “Hello!");

Slide 5

Slide 5 text

SYNTAXOPTIONS Compact Much less code compared to anonymous classes One liners No need to use “return” for one line statements Same scope You can use “this” inside a lambda expression, they also captures variables (by making them effective final). // Inline without an argument () -> "Hello" // With code block as a body () -> { String name = "John"; return "Hello " + name; } // With parameter lastName -> "Mr. " + lastName // With explicit type of parameters (int a, int b) -> a + b @bsideup

Slide 6

Slide 6 text

STREAMS @bsideup

Slide 7

Slide 7 text

STREAMS @bsideup filter sorted map collect Predicate Comparator Function Data

Slide 8

Slide 8 text

STREAMS @bsideup filter sorted map collect Predicate Comparator Function Data 1 2 3

Slide 9

Slide 9 text

STREAMS @bsideup filter sorted map collect Predicate Comparator Function Data 1 3 2

Slide 10

Slide 10 text

STREAMS @bsideup filter sorted map collect Predicate Comparator Function Data 1 3

Slide 11

Slide 11 text

STREAMS @bsideup filter sorted map collect Predicate Comparator Function Data 1 3

Slide 12

Slide 12 text

STREAMS @bsideup filter sorted map collect Predicate Comparator Function Data

Slide 13

Slide 13 text

CODE List items = ...; items.stream() .filter(item -> item.getType() == Type.CIRCLE) .sorted(Comparator.comparing(Item::getId)) .map(item -> "Mr. " + item.getName()) .collect(Collectors.toList()) @bsideup

Slide 14

Slide 14 text

CODE List items = ...; items.stream() .filter(item -> item.getType() == Type.CIRCLE) .sorted(Comparator.comparing(Item::getId)) .map(item -> "Mr. " + item.getName()) .collect(Collectors.toList()) @bsideup Predicate

Slide 15

Slide 15 text

CODE List items = ...; items.stream() .filter(item -> item.getType() == Type.CIRCLE) .sorted(Comparator.comparing(Item::getId)) .map(item -> "Mr. " + item.getName()) .collect(Collectors.toList()) @bsideup Comparator

Slide 16

Slide 16 text

CODE List items = ...; items.stream() .filter(item -> item.getType() == Type.CIRCLE) .sorted(Comparator.comparing(Item::getId)) .map(item -> "Mr. " + item.getName()) .collect(Collectors.toList()) @bsideup Mapper

Slide 17

Slide 17 text

CODE List items = ...; items.stream() .filter(item -> item.getType() == Type.CIRCLE) .sorted(Comparator.comparing(Item::getId)) .map(item -> "Mr. " + item.getName()) .collect(Collectors.toList()) @bsideup Collector

Slide 18

Slide 18 text

Puzzles @bsideup

Slide 19

Slide 19 text

CASE1SIMPLE @bsideup int maximum = Integer.MIN_VALUE; for (Person person : persons) { if (person.age() > maximum) { maximum = person.age(); } } assert maximum == maxAge;

Slide 20

Slide 20 text

CASE1SIMPLE @bsideup int maximum = Integer.MIN_VALUE; for (Person person : persons) { if (person.age() > maximum) { maximum = person.age(); } } assert maximum == maxAge; int maximum = persons.stream() .mapToInt(Person::age) .max() .orElse(Integer.MIN_VALUE); assert maximum == maxAge;

Slide 21

Slide 21 text

CASE1BENCHMARK Ops/s 1 000 10 000 100 000 1 000 000 544 223 563232 Old Streams @bsideup Explanation Old implementation is simple For loop Streams still provide a good performance

Slide 22

Slide 22 text

CASE2”LESS CODE” @bsideup Map> personsByAge = new HashMap<>(); for (Person person : persons) { int age = person.age(); List persons = personsByAge.get(age); if (persons == null) { persons = new ArrayList<>(); personsByAge.put(age, persons); } persons.add(person); } assert personsByAge.get(24).size() == 10;

Slide 23

Slide 23 text

CASE2”LESS CODE” @bsideup Map> personsByAge = new HashMap<>(); for (Person person : persons) { int age = person.age(); List persons = personsByAge.get(age); if (persons == null) { persons = new ArrayList<>(); personsByAge.put(age, persons); } persons.add(person); } assert personsByAge.get(24).size() == 10; Map> personsByAge = persons .stream() .collect( Collectors.groupingBy(Person::age) ); assert personsByAge.get(24).size() == 10;

Slide 24

Slide 24 text

CASE2BENCHMARK Ops/s 1 000 10 000 100 000 42966 45024 Old Streams @bsideup Explanation Collections are already heavily optimised Streams were designed and optimised with concurrency in mind, so they can handle concurrent grouping, old code is not

Slide 25

Slide 25 text

CASE3”LEGACY” @bsideup Map> agesBySex = new HashMap<>(); for (Person person : persons) { List ages = agesBySex.get(person.sex()); if (ages == null) { ages = new ArrayList<>(); agesBySex.put(person.sex(), ages); } ages.add(person.age()); } Map averageAgeBySex = new HashMap<>(); for (Map.Entry> entry : agesBySex.entrySet()) { double sum = 0; for (Integer age : entry.getValue()) { sum += age; } averageAgeBySex.put(entry.getKey(), sum / (double) entry.getValue().size()); }

Slide 26

Slide 26 text

CASE3”LEGACY” @bsideup Map> agesBySex = new HashMap<>(); for (Person person : persons) { List ages = agesBySex.get(person.sex()); if (ages == null) { ages = new ArrayList<>(); agesBySex.put(person.sex(), ages); } ages.add(person.age()); } Map averageAgeBySex = new HashMap<>(); for (Map.Entry> entry : agesBySex.entrySet()) { double sum = 0; for (Integer age : entry.getValue()) { sum += age; } averageAgeBySex.put(entry.getKey(), sum / (double) entry.getValue().size()); } Map averageAgeBySex = persons.stream() .collect( Collectors.groupingBy( Person::sex, Collectors.averagingDouble(Person::age) ) );

Slide 27

Slide 27 text

CASE3BENCHMARK Ops/s 0 26 667 53 333 80 000 68454 44274 Old Streams @bsideup Explanation Not a good quality of old code, but one of the most popular ways of doing such things Heavily optimised algorithms under Stream implementation

Slide 28

Slide 28 text

CASE4”NULL HELL” @bsideup Person person = getPersonFromCache(); if (person == null) { person = getPersonFromDatabase(); if (person == null) { person = createPerson(); } } if (person != null) { blackhole.consume(person); }

Slide 29

Slide 29 text

CASE4”NULL HELL” @bsideup Person person = getPersonFromCache(); if (person == null) { person = getPersonFromDatabase(); if (person == null) { person = createPerson(); } } if (person != null) { blackhole.consume(person); } Stream.>of( this::getPersonFromCache, this::getPersonFromDatabase, this::createPerson ) .map(Supplier::get) .filter(Objects::nonNull) .findFirst() .ifPresent(blackhole::consume);

Slide 30

Slide 30 text

CASE4BENCHMARK Ops/s 90 000 91 500 93 000 91939 91077 Old Streams @bsideup Explanation Magic :D JIT optimisations for Optional

Slide 31

Slide 31 text

THANKYOU! @bsideup