78
1 Ready for "Functional Programming" with Java 8 ? Yanai Franchi , Tikal

Fp java8

Embed Size (px)

DESCRIPTION

The slides from Tikal Meetup - http://www.meetup.com/full-stack-developer-il/events/188639342/

Citation preview

Page 1: Fp java8

1

Ready for "Functional Programming"

with Java 8 ?

Yanai Franchi , Tikal

Page 2: Fp java8

2

Agenda

● First-Class Functions● FP with Streams● Working Concurrency● Demo

Page 3: Fp java8

3

First Class Functions

Page 4: Fp java8

4

Our Inventory = List of Apples

Page 5: Fp java8

5

Class Apple

● getColor() : String● getWeight() : int

Page 6: Fp java8

6

Green Apples, Please...

Page 7: Fp java8

7

Green Apples, Please...

Page 8: Fp java8

8

Filter Green Apples

public static List<Apple> filterGreenApples(List<Apple> inventory){

List<Apple> result = new ArrayList<>();

for (Apple apple: inventory){

if (apple.getColor().equals(“green”)) {

result.add(apple);

}

}

return result;

}

Starts empty, add green apples

one by one

Select only green apples

Page 9: Fp java8

9

Now Heavy Ones , Please...

Page 10: Fp java8

10

Easy...We Copy&Paste :(

public static List<Apple> filterHeavyApples(List<Apple> inventory){

List<Apple> result = new ArrayList<>();

for (Apple apple: inventory){

if (apple.getWeight() > 150) {

result.add(apple);

}

}

return result;

}

We only change the predicate

Page 11: Fp java8

11

What's the Difference ?

public static List<Apple> filterGreenApples

(List<Apple> inventory){

List<Apple> result = new ArrayList<>();

for (Apple apple: inventory){

if (apple.getColor.equals(“green”)) {

result.add(apple);

}

}

return result;

}

public static List<Apple> filterHeavyApples

(List<Apple> inventory){

List<Apple> result = new ArrayList<>();

for (Apple apple: inventory){

if (apple.getWeight() > 150) {

result.add(apple);

}

}

return result;

}

Page 12: Fp java8

12

Page 13: Fp java8

13

New Behavior

BehaviorParameterization

Output

apple.getWeight > 150 apple.getColor.equals(“green”)

Heavy Apples Green Apples

static List<Apple> filterApples(

List<Apple> inventory, Predicate<Apple> p) {

List<Apple> result = new ArrayList<>();

for (Apple apple: inventory){

if (p.test(apple)) {

result.add(apple);

}

}

return result;

}

Page 14: Fp java8

14

In Practice, Our Client...Yuck :(

List<Apple> greenApples =

filterApples(inventory,new Predicate<Apple>() {

@Override

public boolean test(Apple a) {

return a.getColor().equals(“green”);

}

});

A lot of “noise”

Page 15: Fp java8

15

Page 16: Fp java8

16

Java8 Lambda to Rescue

filterApples(inventory,

(Apple a) → {return a.getColor().equals(“green”)});

Page 17: Fp java8

17

Sending Lambda Expression

filterApples(inventory,

(Apple a) → a.getColor().equals(“green”));

Page 18: Fp java8

18

Making it Shorter

filterApples(inventory,

(a) → a.getColor().equals(“green”));

Apple Type is Inferred

Page 19: Fp java8

19

...Even Shorter

filterApples(inventory,

a → a.getColor().equals(“green”));

Page 20: Fp java8

20

“Capture” Values (a.k.a “Closure”)

String color = “green”;

filterApples(inventory,

a → a.getColor().equals(color));

Implicitly final

Page 21: Fp java8

21

Page 22: Fp java8

22

Anonymous Function that Can Be Passed Around

filterApples(inventory,

a → a.equals.getColor(“green”));

Page 23: Fp java8

23

Switch to Method Reference

filterApples(inventory,a → a.equals.getColor(“green”));

Page 24: Fp java8

24

Switch to Method Reference

filterApples(inventory,Apple::isGreen);

filterApples(inventory,a → a.getColor().equals(“green”));

isGreen is declared in Apple

Page 25: Fp java8

25

Until Now...

Page 26: Fp java8

26

...In Java 8

Page 27: Fp java8

27

Lambda Type = Functional Interface

Predicate<Apple> redApple = a → a.getColor().equals(“red”);

Predicate is the type of our lambda

Page 28: Fp java8

28

Meet Functional Interface

@FunctionalInterface

public interface Predicate<T>{

boolean test(T t);

}

Just Marker

● Definition: a functional interface is an interface with one abstract method

● Single Abstract Method (SAM) type

SAM

Page 29: Fp java8

29

Default Methods Implementations in Interface

@FunctionalInterface

public interface Predicate<T>{

boolean test(T t);

default Predicate<T> negate(){

return (t) -> !test(t);

}

default or(Predicate<? super T> other){

return (t) -> test(t) ||

other.test(t);

}

default and(Predicate<? super T> other){

return (t) -> test(t) &&

other.test(t);

}

}

Default Method

Page 30: Fp java8

30

Existing Function Interfaces

interface Comparator<T> { boolean compare(T x, T y); }

(int x, int y) → x - y ;

interface FileFilter { boolean accept(File x); }

f → f.isDirectory()

interface Runnable { void run(); }

() → someCode();

interface ActionListener{ void actionPerformed(ActionEvent ae); }

(ae -> log.debug(“Got it!”));

Page 31: Fp java8

31

New Function Interfaces

● Predicate<T> : boolean test(T t);

– Determine if the input of type T matches some criteria : ● Consumer<T> : void accept(T t);

– Accept a single input argument of type T, and return no result :

● Supplier<T> : T get();

– A factory that’s expected to return either a new instance or a pre-created instance :

● Function<T, R> : R apply(T t);

– Apply a function to the input type T, generating a result of type R

Page 32: Fp java8

32

Page 33: Fp java8

33

Sorting with Anonymous Class

inventory.sort(new Comparator<Apple>() {

public int compare(Apple a1, Apple a2){

return a1.getWeight().compareTo(a2.getWeight());

}

});

High OrderFunction

Page 34: Fp java8

34

Sort with Lambda

inventory.sort( (a1,a2) → a1.getWeight().compareTo(a2.getWeight()));

Page 35: Fp java8

35

Using “comparing” Method

Comparator InterfaceStatic method

inventory.sort(comparing(Apple::getWeight));

Static method that accept Function and return Comparator

Page 36: Fp java8

36

FP with Streams

Page 37: Fp java8

37

Dish

Row 1 Row 2 Row 3 Row 40

2

4

6

8

10

12

Column 1

Column 2

Column 3

● String getName()● boolean isVegeterian()● int getCalories()● Type getType()● List<String> getNickNames()● CaloricLevel getCaloricLevel()

Page 38: Fp java8

38

Three Fat Dishes - Imperative

● Client controls iteration

● Inherently serial: iterate from beginning to end

● Not thread-safe because business logic is stateful (mutable accumulator variable)

● Frustratingly imperative

List<String> fatsDishes = ...

int count = 0;

for(Dish d : menu){

if (count > 3)

break;

if(d.getCalories > 300){

count++;

fatsDishes.add(d.getName());

}

}

External Iteration

Page 39: Fp java8

39

Page 40: Fp java8

40

FP → Declarative CodeList<String> fatDishes =

menu.stream()

.filter(d -> d.getCalories() > 300)

.map(Dish::getName)

.limit(3)

.collect(toList());

Page 41: Fp java8

41

Page 42: Fp java8

42

But What is a Stream ?

● A sequence of elements from a source that supports aggregate operations– Aimed for computations and aggregations (In

contrary to collections which are data structures)

– Consume from a data-providing source such as Collections, Arrays, or IO resources.

– Support SQL-like operations and common operations from functional programing languages such as filter, map, reduce, find, match, sorted etc.

Page 43: Fp java8

43

Creating Stream

● IntStream.range(1, 100)

● Stream.of("Java 8 ", "Lambdas");

● File.list(Paths.get("some-folder"))● Files.lines(Paths.get("data.txt"))● myList.parallelStream()

● Create infinite stream– Iterate

● Stream.iterate(0, n -> n + 2)● Stream.iterate(new int[]{0, 1}, t -> new int[]{t[1], t[0]+t[1]})

– Stream.generate(Math::random)

Page 44: Fp java8

44

High Order Function - map

● <R> Stream<R> map(Function<? super T, ? extends R> mapper);

● Apply the mapper function on each element of the stream , and create a new stream from its outputs by placing the results of the mapper on the new stream

● map(Dish::getName). Converts a Stream of dishes to Stream names (Strings), by applying Dish.getName() on the elements.

Page 45: Fp java8

45

High Order Function - flatMap

● <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

● Apply the mapper function on each element of the stream and create a new stream from its outputs, by placing contents of the mapped streams in the new stream

● menu.stream().flatMap(d → d.getNickNames().stream())

– Converts a Stream of dishes to Stream of nickNames (String), by replacing all nicknames as elements of the stream.

Page 46: Fp java8

46

Limit - “Short Circuit”List<String> names = menu.stream()

.filter(d -> {System.out.println("filtering" + d);

return d.getCalories() > 300;})

.map(d -> {System.out.println("mapping" + d);

return d.getName();})

.limit(3)

.collect(toList());

filtering pork

mapping pork

filtering beef

filtering chicken

mapping chicken

filtering eggfiltering spaghetti

mapping spaghetti

Short Circuit:Stopped printing

after 3 dishes

Page 47: Fp java8

47

Finding & Matching - “Short Circuit”

● No need to process the whole stream● As soon as an element is found a result can be

produced.● Finding Operations:

– anyMatch, noneMatch, findFirst and findAny

boolean found =

menu.stream().anyMatch(Dish::isVegatarian)

Page 48: Fp java8

48

Group Dishes by Type - Imperative

Map<Dish.Type, List<Dish>> groupDishesByTypeMap =

new HashMap<>();

for (Dish dish : menu) {

final Type type = dish.getType();

List<Dish> dishesForType = groupDishesByTypeMap.get(type);

if(dishesForType == null){

dishesForType = new ArrayList<>();

groupDishesByTypeMap.put(type, dishesForType);

}

dishesForType.add(dish);

}

return groupDishesByTypeMap;

Page 49: Fp java8

49

Grouping with FP - Declarative

Map<Dish.Type, List<Dish>> groupDishesByTypeMap =

menu.stream().collect(groupingBy(Dish::getType));

Collectors static method

Page 50: Fp java8

50

Count Dishes Per Type

Map<Dish.Type, Long> typesCount = menu.stream().collect(

groupingBy(Dish::getType,counting()));

{MEAT=3, FISH=2, OTHER=4}Output

Collectors static method

Page 51: Fp java8

51

One More Level...

Page 52: Fp java8

52

One More Level...Map<Dish.Type, Map<CaloricLevel, List<Dish>>> groupDishesByTypeAndCaloricLevel = menu.stream() .collect(groupingBy( Dish::getType, groupingBy(Dish::getCaloricLevel)));

{MEAT={DIET=[chicken], NORMAL=[beef], FAT=[pork]},FISH={DIET=[prawns], NORMAL=[salmon]},OTHER={DIET=[rice, seasonal fruit], NORMAL=[french fries, pizza]}

}

Output

Page 53: Fp java8

53

Working Concurrency

Page 54: Fp java8

54

Process Dishes in Parallel

● Uses the ForkJoin framework behind the scenes● By Default number of threads is as the number of

processors● Will NOT keep the original order of the stream

menu.parallelStream().forEach(Dish::heavyCalculation);

Page 55: Fp java8

55

Total Calories in Menu – Imperative

int i=0;for (Dish dish : menu) { i += dish.getCalories();}

Side Effect

Page 56: Fp java8

56

We Know What To Do...

● The only difference is the reduction function● For min, max, avg etc – The code will look the

same

int i=0;for (Dish dish : menu) { i += dish.getCalories();}

int i=0;for (Dish dish : menu) { i = Math.max(dish.getCalories(),i);}

Page 57: Fp java8

57

Move To FP – Bad Way :(

Accumulator accumulator = new Accumulator();menu.stream()

.map(Dish::getCalories)

.forEach(accumulator::add);accumulator.getTotal();

class Accumulator { private long total = 0;

long getTotal(){return total);

public void add(long value) { total += value; } } Side Effect

Page 58: Fp java8

58

Break Concurrency!!! :(

Accumulator accumulator = new Accumulator();menu.parallelStream()

.map(Dish::getCalories)

.forEach(accumulator::add)accumulator.getTotal();

Different resultFor the same input

Page 59: Fp java8

59

Reducing

Row 1 Row 2 Row 3 Row 40

2

4

6

8

10

12

Column 1

Column 2

Column 3

menu.parallelStream().map(Dish::getCalories).sum();

menu.parallelStream().map(Dish::getCalories).reduce(0,Integer:sum);

=

Page 60: Fp java8

60

FP - Data is Immutable

● Once object is created, it can't be changed.● If you need to change an object, make a copy.

– Enables concurrency

– Rollback of data

– Simplicity

Page 61: Fp java8

61

Mutable Tree – What's Wrong ?class Tree {

private String key;private int val;private Tree left, right;public Tree(String k, int v, Tree l, Tree r) {

key = k; val = v; left = l; right = r;}

}

public static Tree update(String k, int newval, Tree t) {if (t == null)

t = new Tree(k, newval, null, null);else if (k.equals(t.key))

t.val = newval;else if (k.compareTo(t.key) < 0)

t.left = update(k, newval, t.left);else

t.right = update(k, newval, t.right);return t;

}

Page 62: Fp java8

62

Concurrency Problems

● Every user wants to share the identical data structure and see updates caused by any part of the program.

● Hence it is vital (but often overlooked) in nonfunctional code that whenever we add some form of structured value to a tree then we copy it - Who knows, someone may later assume they can update it.

● In Functional approach – Share storage for common parts of structure.

Page 63: Fp java8

63

Marry22

Emily20 Titan

29

Alan50

Georgie23

Raoul25

Persistent Tree

Page 64: Fp java8

64

Marry22

Emily20 Titan

29

Alan50

Georgie23

Raoul25

Input : “will”, 26

Persistent Tree

Page 65: Fp java8

65

Marry22

Emily20 Titan

29

Alan50

Georgie23

Raoul25

Marry22

Titan29

Will26

Output of updateInput : “will”, 26

Persistent Tree

Page 66: Fp java8

66

Persistent Treeclass Tree {

private String key;private int val;private Tree left, right;public Tree(String k, int v, Tree l, Tree r) {

key = k; val = v; left = l; right = r;}

}

public static Tree fupdate(String k, int newval, Tree t) { return (t == null) ? new Tree(k, newval, null, null) : k.equals(t.key) ? new Tree(k, newval, t.left, t.right) : k.compareTo(t.key) < 0 ? new Tree(t.key, t.val, fupdate(k,newval, t.left), t.right) : new Tree(t.key, t.val, t.left, fupdate(k,newval, t.right));}

Page 67: Fp java8

67

Analyze Multiple Apache Logs

Page 68: Fp java8

68

Analyze Multiple Apache Logs

● Host, Time, Request, Response, Resp-Length199.72.81.55 - - [02/Jul/1995:00:00:01 -0400] "GET /history/apollo/ HTTP/1.0" 200 6245unicomp6.unicomp.net - - [02/Jul/1995:00:00:06 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985199.120.110.21 - - [01/Jul/1995:00:00:09 -0400] "GET /shuttle/missions/sts-73/mission-sts-73.html HTTP/1.0" 200 4085burger.letters.com - - [01/Jul/1995:00:00:11 -0400] "GET /shuttle/countdown/liftoff.html HTTP/1.0" 304 0199.120.110.21 - - [01/Jul/1995:00:00:11 -0400] "GET /shuttle/missions/sts-73/sts-73-patch-small.gif HTTP/1.0" 200 4179burger.letters.com - - [01/Jul/1995:00:00:12 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 304 0burger.letters.com - - [01/Jul/1995:00:00:12 -0400] "GET /shuttle/countdown/video/livevideo.gif HTTP/1.0" 200 0205.212.115.106 - - [01/Jul/1995:00:00:12 -0400] "GET /shuttle/countdown/countdown.html HTTP/1.0" 200 3985d104.aa.net - - [01/Jul/1995:00:00:13 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985129.94.144.152 - - [01/Jul/1995:00:00:13 -0400] "GET / HTTP/1.0" 200 7074unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /shuttle/countdown/count.gif HTTP/1.0" 200 40310unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 200 786unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /images/KSC-logosmall.gif HTTP/1.0" 200 1204d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /shuttle/countdown/count.gif HTTP/1.0" 200 40310d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 200 786d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /images/KSC-logosmall.gif HTTP/1.0" 200 1204129.94.144.152 - - [01/Jul/1995:00:00:17 -0400] "GET /images/ksclogo-medium.gif HTTP/1.0" 304 0199.120.110.21 - - [01/Jul/1995:00:00:17 -0400] "GET /images/launch-logo.gif HTTP/1.0" 200 1713ppptky391.asahi-net.or.jp - - [01/Jul/1995:00:00:18 -0400] "GET /facts/about_ksc.html HTTP/1.0" 200 3977ppptky391.asahi-net.or.jp - - [01/Jul/1995:00:00:19 -0400] "GET /images/launchpalms-small.gif HTTP/1.0" 200 11473205.189.154.54 - - [01/Jul/1995:00:00:24 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985…...

199.72.81.55 - - [02/Jul/1995:00:00:01 -0400] "GET /history/apollo/ HTTP/1.0" 200 6245unicomp6.unicomp.net - - [02/Jul/1995:00:00:06 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985199.120.110.21 - - [01/Jul/1995:00:00:09 -0400] "GET /shuttle/missions/sts-73/mission-sts-73.html HTTP/1.0" 200 4085burger.letters.com - - [01/Jul/1995:00:00:11 -0400] "GET /shuttle/countdown/liftoff.html HTTP/1.0" 304 0199.120.110.21 - - [01/Jul/1995:00:00:11 -0400] "GET /shuttle/missions/sts-73/sts-73-patch-small.gif HTTP/1.0" 200 4179burger.letters.com - - [01/Jul/1995:00:00:12 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 304 0burger.letters.com - - [01/Jul/1995:00:00:12 -0400] "GET /shuttle/countdown/video/livevideo.gif HTTP/1.0" 200 0205.212.115.106 - - [01/Jul/1995:00:00:12 -0400] "GET /shuttle/countdown/countdown.html HTTP/1.0" 200 3985d104.aa.net - - [01/Jul/1995:00:00:13 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985129.94.144.152 - - [01/Jul/1995:00:00:13 -0400] "GET / HTTP/1.0" 200 7074unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /shuttle/countdown/count.gif HTTP/1.0" 200 40310unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 200 786unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /images/KSC-logosmall.gif HTTP/1.0" 200 1204d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /shuttle/countdown/count.gif HTTP/1.0" 200 40310d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 200 786d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /images/KSC-logosmall.gif HTTP/1.0" 200 1204129.94.144.152 - - [01/Jul/1995:00:00:17 -0400] "GET /images/ksclogo-medium.gif HTTP/1.0" 304 0199.120.110.21 - - [01/Jul/1995:00:00:17 -0400] "GET /images/launch-logo.gif HTTP/1.0" 200 1713ppptky391.asahi-net.or.jp - - [01/Jul/1995:00:00:18 -0400] "GET /facts/about_ksc.html HTTP/1.0" 200 3977ppptky391.asahi-net.or.jp - - [01/Jul/1995:00:00:19 -0400] "GET /images/launchpalms-small.gif HTTP/1.0" 200 11473205.189.154.54 - - [01/Jul/1995:00:00:24 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985…...

199.72.81.55 - - [02/Jul/1995:00:00:01 -0400] "GET /history/apollo/ HTTP/1.0" 200 6245unicomp6.unicomp.net - - [02/Jul/1995:00:00:06 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985199.120.110.21 - - [01/Jul/1995:00:00:09 -0400] "GET /shuttle/missions/sts-73/mission-sts-73.html HTTP/1.0" 200 4085burger.letters.com - - [01/Jul/1995:00:00:11 -0400] "GET /shuttle/countdown/liftoff.html HTTP/1.0" 304 0199.120.110.21 - - [01/Jul/1995:00:00:11 -0400] "GET /shuttle/missions/sts-73/sts-73-patch-small.gif HTTP/1.0" 200 4179burger.letters.com - - [01/Jul/1995:00:00:12 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 304 0burger.letters.com - - [01/Jul/1995:00:00:12 -0400] "GET /shuttle/countdown/video/livevideo.gif HTTP/1.0" 200 0205.212.115.106 - - [01/Jul/1995:00:00:12 -0400] "GET /shuttle/countdown/countdown.html HTTP/1.0" 200 3985d104.aa.net - - [01/Jul/1995:00:00:13 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985129.94.144.152 - - [01/Jul/1995:00:00:13 -0400] "GET / HTTP/1.0" 200 7074unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /shuttle/countdown/count.gif HTTP/1.0" 200 40310unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 200 786unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /images/KSC-logosmall.gif HTTP/1.0" 200 1204d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /shuttle/countdown/count.gif HTTP/1.0" 200 40310d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 200 786d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /images/KSC-logosmall.gif HTTP/1.0" 200 1204129.94.144.152 - - [01/Jul/1995:00:00:17 -0400] "GET /images/ksclogo-medium.gif HTTP/1.0" 304 0199.120.110.21 - - [01/Jul/1995:00:00:17 -0400] "GET /images/launch-logo.gif HTTP/1.0" 200 1713ppptky391.asahi-net.or.jp - - [01/Jul/1995:00:00:18 -0400] "GET /facts/about_ksc.html HTTP/1.0" 200 3977ppptky391.asahi-net.or.jp - - [01/Jul/1995:00:00:19 -0400] "GET /images/launchpalms-small.gif HTTP/1.0" 200 11473205.189.154.54 - - [01/Jul/1995:00:00:24 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985…...

Page 69: Fp java8

69

Requirements

● Answer the following:– Are there any errors ?

– Are there any empty-body responses ?

– Find last 5 error

– Count statuses for each day

● Assumptions– Analyze multiple logs in “logs” folder

– Logs files are big and can't fit into memory

Page 70: Fp java8

70

Design

● LogEntry - Entity Class– parse(String line) : LogEntry

● LogAnalyticService – Service Class– streamLogs() : Stream<LogEntry>

● Stream log files in folder● Per Each file Stream lines● Map each line to LogEntry

● anyMatch – Reuse for different predicates● groupBy – For grouping

Page 71: Fp java8

71

Implementationprivate Stream<LogEntry> streamLogs() {

try {

return Files.list(Paths.get(loggingDir)).filter(p -> p.getFileName().endsWith(LOG_EXT)).flatMap(Files::lines).map(LogEntry::parse);

} catch (final Exception e) {throw new RuntimeException(e);

}}

Will not Compile

Page 72: Fp java8

72

private Stream<LogEntry> streamLogs() {try {

return Files.list(Paths.get(loggingDir)).filter(p -> p.getFileName().endsWith(LOG_EXT)).flatMap(this::lines).map(LogEntry::parse);

} catch (final Exception e) {throw new RuntimeException(e);

}}

private Stream<String> lines(final Path path) {try {

return Files.lines(path);} catch (IOException e) {

logger.error("Failed to process "+path,e);return Stream.empty();

}}

Page 73: Fp java8

73

Last Err Logs

return streamLogs() .filter((le) -> le.getResponse() >= 500) .sorted(comparing(LogEntry::getDateTime).reversed()) .limit(5) .collect(toList());

Page 74: Fp java8

74

Sending Predicates

public boolean isAnyWithResponse(int response) {return anyMatch((le) -> le.getResponse() == response);

}

public boolean isAnyEmptyResponse() {return anyMatch((le) -> le.getByteSent() == 0);

}

boolean anyMatch(Predicate<? super LogEntry> predicate) {return streamLogs().anyMatch(predicate);

}

Page 75: Fp java8

75

Grouping Results

Map<LocalDate, Map<Integer, Long>> groupingByDatesThenResponse() {return

streamLogs().collect(groupingBy(

LogEntry::getDate,TreeMap::new, groupingBy(LogEntry::getResponse, counting()))

);}

Ordered Keys

Nested Grouping

Page 76: Fp java8

76

Let's Run it...

Page 77: Fp java8

77

FP Summary

● Functions are first-class citizens– Can be declared as variables, pass as arguments

or be returned by other functions

● Declarative instead of imperative– We tell what to do, instead of how

● Data Immutability– Reduce Side Effect

– Easier path to parallel code

● Java 8 Lambda & Streams enable FP style

Page 78: Fp java8

78

Thank you