SlideShare a Scribd company logo
1
Ready for
"Functional Programming"
with Java 8 ?
Yanai Franchi , Tikal
2
Agenda
● First-Class Functions
● FP with Streams
● Working Concurrency
● Demo
3
First Class Functions
4
Our Inventory = List of Apples
5
Class Apple
● getColor() : String
● getWeight() : int
6
Green Apples, Please...
7
Green Apples, Please...
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
9
Now Heavy Ones , Please...
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
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;
}
12
13
New Behavior
Behavior
Parameterization
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;
}
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”
15
16
Java8 Lambda to Rescue
filterApples(inventory,
(Apple a) → {return a.getColor().equals(“green”)});
17
Sending Lambda Expression
filterApples(inventory,
(Apple a) → a.getColor().equals(“green”));
18
Making it Shorter
filterApples(inventory,
(a) → a.getColor().equals(“green”));
Apple Type is
Inferred
19
...Even Shorter
filterApples(inventory,
a → a.getColor().equals(“green”));
20
“Capture” Values (a.k.a “Closure”)
String color = “green”;
filterApples(inventory,
a → a.getColor().equals(color));
Implicitly final
21
22
Anonymous Function that Can Be
Passed Around
filterApples(inventory,
a → a.equals.getColor(“green”));
23
Switch to Method Reference
filterApples(inventory,a → a.equals.getColor(“green”));
24
Switch to Method Reference
filterApples(inventory,Apple::isGreen);
filterApples(inventory,a → a.getColor().equals(“green”));
isGreen is
declared in Apple
25
Until Now...
26
...In Java 8
27
Lambda Type = Functional Interface
Predicate<Apple> redApple = a → a.getColor().equals(“red”);
Predicate is the
type of our lambda
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
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
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!”));
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
32
33
Sorting with Anonymous Class
inventory.sort(new Comparator<Apple>() {
public int compare(Apple a1, Apple a2){
return a1.getWeight().compareTo(a2.getWeight());
}
});
High Order
Function
34
Sort with Lambda
inventory.sort(
(a1,a2) → a1.getWeight().compareTo(a2.getWeight())
);
35
Using “comparing” Method
Comparator Interface
Static method
inventory.sort(comparing(Apple::getWeight));
Static method that accept
Function and return Comparator
36
FP with Streams
37
Dish
Row 1 Row 2 Row 3 Row 4
0
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()
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
39
40
FP → Declarative Code
List<String> fatDishes =
menu.stream()
.filter(d -> d.getCalories() > 300)
.map(Dish::getName)
.limit(3)
.collect(toList());
41
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.
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)
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.
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.
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 egg
filtering spaghetti
mapping spaghetti
Short Circuit:
Stopped printing
after 3 dishes
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)
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;
49
Grouping with FP - Declarative
Map<Dish.Type, List<Dish>> groupDishesByTypeMap =
menu.stream().collect(groupingBy(Dish::getType));
Collectors static method
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
51
One More Level...
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
53
Working Concurrency
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);
55
Total Calories in Menu – Imperative
int i=0;
for (Dish dish : menu) {
i += dish.getCalories();
}
Side Effect
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);
}
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
58
Break Concurrency!!! :(
Accumulator accumulator = new Accumulator();
menu.parallelStream()
.map(Dish::getCalories)
.forEach(accumulator::add)
accumulator.getTotal();
Different result
For the same input
59
Reducing
Row 1 Row 2 Row 3 Row 4
0
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);
=
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
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;
}
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.
63
Marry
22
Emily
20 Titan
29
Alan
50
Georgie
23
Raoul
25
Persistent Tree
64
Marry
22
Emily
20 Titan
29
Alan
50
Georgie
23
Raoul
25
Input : “will”, 26
Persistent Tree
65
Marry
22
Emily
20 Titan
29
Alan
50
Georgie
23
Raoul
25
Marry
22
Titan
29
Will
26
Output of updateInput : “will”, 26
Persistent Tree
66
Persistent Tree
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 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));
}
67
Analyze Multiple Apache Logs
68
Analyze Multiple Apache Logs
● Host, Time, Request, Response, Resp-Length
199.72.81.55 - - [02/Jul/1995:00:00:01 -0400] "GET /history/apollo/ HTTP/1.0" 200 6245
unicomp6.unicomp.net - - [02/Jul/1995:00:00:06 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985
199.120.110.21 - - [01/Jul/1995:00:00:09 -0400] "GET /shuttle/missions/sts-73/mission-sts-73.html HTTP/1.0" 200 4085
burger.letters.com - - [01/Jul/1995:00:00:11 -0400] "GET /shuttle/countdown/liftoff.html HTTP/1.0" 304 0
199.120.110.21 - - [01/Jul/1995:00:00:11 -0400] "GET /shuttle/missions/sts-73/sts-73-patch-small.gif HTTP/1.0" 200 4179
burger.letters.com - - [01/Jul/1995:00:00:12 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 304 0
burger.letters.com - - [01/Jul/1995:00:00:12 -0400] "GET /shuttle/countdown/video/livevideo.gif HTTP/1.0" 200 0
205.212.115.106 - - [01/Jul/1995:00:00:12 -0400] "GET /shuttle/countdown/countdown.html HTTP/1.0" 200 3985
d104.aa.net - - [01/Jul/1995:00:00:13 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985
129.94.144.152 - - [01/Jul/1995:00:00:13 -0400] "GET / HTTP/1.0" 200 7074
unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /shuttle/countdown/count.gif HTTP/1.0" 200 40310
unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 200 786
unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /images/KSC-logosmall.gif HTTP/1.0" 200 1204
d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /shuttle/countdown/count.gif HTTP/1.0" 200 40310
d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 200 786
d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /images/KSC-logosmall.gif HTTP/1.0" 200 1204
129.94.144.152 - - [01/Jul/1995:00:00:17 -0400] "GET /images/ksclogo-medium.gif HTTP/1.0" 304 0
199.120.110.21 - - [01/Jul/1995:00:00:17 -0400] "GET /images/launch-logo.gif HTTP/1.0" 200 1713
ppptky391.asahi-net.or.jp - - [01/Jul/1995:00:00:18 -0400] "GET /facts/about_ksc.html HTTP/1.0" 200 3977
ppptky391.asahi-net.or.jp - - [01/Jul/1995:00:00:19 -0400] "GET /images/launchpalms-small.gif HTTP/1.0" 200 11473
205.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 6245
unicomp6.unicomp.net - - [02/Jul/1995:00:00:06 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985
199.120.110.21 - - [01/Jul/1995:00:00:09 -0400] "GET /shuttle/missions/sts-73/mission-sts-73.html HTTP/1.0" 200 4085
burger.letters.com - - [01/Jul/1995:00:00:11 -0400] "GET /shuttle/countdown/liftoff.html HTTP/1.0" 304 0
199.120.110.21 - - [01/Jul/1995:00:00:11 -0400] "GET /shuttle/missions/sts-73/sts-73-patch-small.gif HTTP/1.0" 200 4179
burger.letters.com - - [01/Jul/1995:00:00:12 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 304 0
burger.letters.com - - [01/Jul/1995:00:00:12 -0400] "GET /shuttle/countdown/video/livevideo.gif HTTP/1.0" 200 0
205.212.115.106 - - [01/Jul/1995:00:00:12 -0400] "GET /shuttle/countdown/countdown.html HTTP/1.0" 200 3985
d104.aa.net - - [01/Jul/1995:00:00:13 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985
129.94.144.152 - - [01/Jul/1995:00:00:13 -0400] "GET / HTTP/1.0" 200 7074
unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /shuttle/countdown/count.gif HTTP/1.0" 200 40310
unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 200 786
unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /images/KSC-logosmall.gif HTTP/1.0" 200 1204
d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /shuttle/countdown/count.gif HTTP/1.0" 200 40310
d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 200 786
d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /images/KSC-logosmall.gif HTTP/1.0" 200 1204
129.94.144.152 - - [01/Jul/1995:00:00:17 -0400] "GET /images/ksclogo-medium.gif HTTP/1.0" 304 0
199.120.110.21 - - [01/Jul/1995:00:00:17 -0400] "GET /images/launch-logo.gif HTTP/1.0" 200 1713
ppptky391.asahi-net.or.jp - - [01/Jul/1995:00:00:18 -0400] "GET /facts/about_ksc.html HTTP/1.0" 200 3977
ppptky391.asahi-net.or.jp - - [01/Jul/1995:00:00:19 -0400] "GET /images/launchpalms-small.gif HTTP/1.0" 200 11473
205.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 6245
unicomp6.unicomp.net - - [02/Jul/1995:00:00:06 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985
199.120.110.21 - - [01/Jul/1995:00:00:09 -0400] "GET /shuttle/missions/sts-73/mission-sts-73.html HTTP/1.0" 200 4085
burger.letters.com - - [01/Jul/1995:00:00:11 -0400] "GET /shuttle/countdown/liftoff.html HTTP/1.0" 304 0
199.120.110.21 - - [01/Jul/1995:00:00:11 -0400] "GET /shuttle/missions/sts-73/sts-73-patch-small.gif HTTP/1.0" 200 4179
burger.letters.com - - [01/Jul/1995:00:00:12 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 304 0
burger.letters.com - - [01/Jul/1995:00:00:12 -0400] "GET /shuttle/countdown/video/livevideo.gif HTTP/1.0" 200 0
205.212.115.106 - - [01/Jul/1995:00:00:12 -0400] "GET /shuttle/countdown/countdown.html HTTP/1.0" 200 3985
d104.aa.net - - [01/Jul/1995:00:00:13 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985
129.94.144.152 - - [01/Jul/1995:00:00:13 -0400] "GET / HTTP/1.0" 200 7074
unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /shuttle/countdown/count.gif HTTP/1.0" 200 40310
unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 200 786
unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /images/KSC-logosmall.gif HTTP/1.0" 200 1204
d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /shuttle/countdown/count.gif HTTP/1.0" 200 40310
d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 200 786
d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /images/KSC-logosmall.gif HTTP/1.0" 200 1204
129.94.144.152 - - [01/Jul/1995:00:00:17 -0400] "GET /images/ksclogo-medium.gif HTTP/1.0" 304 0
199.120.110.21 - - [01/Jul/1995:00:00:17 -0400] "GET /images/launch-logo.gif HTTP/1.0" 200 1713
ppptky391.asahi-net.or.jp - - [01/Jul/1995:00:00:18 -0400] "GET /facts/about_ksc.html HTTP/1.0" 200 3977
ppptky391.asahi-net.or.jp - - [01/Jul/1995:00:00:19 -0400] "GET /images/launchpalms-small.gif HTTP/1.0" 200 11473
205.189.154.54 - - [01/Jul/1995:00:00:24 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985
…
...
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
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
71
Implementation
private 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
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();
}
}
73
Last Err Logs
return streamLogs()
.filter((le) -> le.getResponse() >= 500)
.sorted(comparing(LogEntry::getDateTime).reversed())
.limit(5)
.collect(toList());
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);
}
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
76
Let's Run it...
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
78
Thank you

More Related Content

KEY
関数潮流(Function Tendency)
PDF
Functional Programming for OO Programmers (part 2)
ODP
Introduction to R
PDF
JDD2015: Functional programing and Event Sourcing - a pair made in heaven - e...
PDF
Functional programming basics
PDF
The Ring programming language version 1.5.3 book - Part 10 of 184
PDF
Introduction to Python
PDF
The Ring programming language version 1.5.2 book - Part 33 of 181
関数潮流(Function Tendency)
Functional Programming for OO Programmers (part 2)
Introduction to R
JDD2015: Functional programing and Event Sourcing - a pair made in heaven - e...
Functional programming basics
The Ring programming language version 1.5.3 book - Part 10 of 184
Introduction to Python
The Ring programming language version 1.5.2 book - Part 33 of 181

What's hot (20)

PDF
The Ring programming language version 1.2 book - Part 23 of 84
PDF
Programmation fonctionnelle en JavaScript
PDF
PHP and MySQL Tips and tricks, DC 2007
PDF
The Ring programming language version 1.5.4 book - Part 34 of 185
PDF
JAVA 8 : Migration et enjeux stratégiques en entreprise
PDF
Python Workshop Part 2. LUG Maniapl
PDF
20170509 rand db_lesugent
PDF
The Ring programming language version 1.9 book - Part 42 of 210
PDF
Javascript
PDF
Model-Driven Software Development - Static Analysis & Error Checking
PDF
The Ring programming language version 1.5.1 book - Part 32 of 180
PDF
An introduction to functional programming with go
PDF
Swift에서 꼬리재귀 사용기 (Tail Recursion)
PPTX
Python Programming Essentials - M12 - Lists
PDF
dotSwift 2016 : Beyond Crusty - Real-World Protocols
PDF
7 Habits For a More Functional Swift
PDF
Kotlin Advanced - Apalon Kotlin Sprint Part 3
PDF
Python programming : Arrays
PDF
The Ring programming language version 1.5.3 book - Part 34 of 184
The Ring programming language version 1.2 book - Part 23 of 84
Programmation fonctionnelle en JavaScript
PHP and MySQL Tips and tricks, DC 2007
The Ring programming language version 1.5.4 book - Part 34 of 185
JAVA 8 : Migration et enjeux stratégiques en entreprise
Python Workshop Part 2. LUG Maniapl
20170509 rand db_lesugent
The Ring programming language version 1.9 book - Part 42 of 210
Javascript
Model-Driven Software Development - Static Analysis & Error Checking
The Ring programming language version 1.5.1 book - Part 32 of 180
An introduction to functional programming with go
Swift에서 꼬리재귀 사용기 (Tail Recursion)
Python Programming Essentials - M12 - Lists
dotSwift 2016 : Beyond Crusty - Real-World Protocols
7 Habits For a More Functional Swift
Kotlin Advanced - Apalon Kotlin Sprint Part 3
Python programming : Arrays
The Ring programming language version 1.5.3 book - Part 34 of 184
Ad

Viewers also liked (20)

PPTX
Intro to Functional Programming in Scala
PDF
Clojure - LISP on the JVM
PPTX
PPTX
Lambda functions in java 8
PPTX
Functional Programming in Java
PDF
Functional Programming in Java 8 - Exploiting Lambdas
PDF
2java Oop
PDF
2ndQuarter2ndMeeting(formatting number)
PPTX
Week 5 java script functions
PDF
java script functions, classes
PDF
TM 2nd qtr-3ndmeeting(java script-functions)
PDF
Java Script - Object-Oriented Programming
PPTX
02 java programming basic
PDF
Understanding Java 8 Lambdas and Streams - Part 1 - Lambda Calculus, Lambda...
PPT
JavaScript Functions
PDF
Functional programming with Java 8
PDF
Functional Javascript
PDF
JavaScript Functions
PDF
Functional programming in java
Intro to Functional Programming in Scala
Clojure - LISP on the JVM
Lambda functions in java 8
Functional Programming in Java
Functional Programming in Java 8 - Exploiting Lambdas
2java Oop
2ndQuarter2ndMeeting(formatting number)
Week 5 java script functions
java script functions, classes
TM 2nd qtr-3ndmeeting(java script-functions)
Java Script - Object-Oriented Programming
02 java programming basic
Understanding Java 8 Lambdas and Streams - Part 1 - Lambda Calculus, Lambda...
JavaScript Functions
Functional programming with Java 8
Functional Javascript
JavaScript Functions
Functional programming in java
Ad

Similar to Fp java8 (20)

PDF
Gdg almaty. Функциональное программирование в Java 8
PDF
Java 8: more readable and flexible code
PPTX
Exploring Streams and Lambdas in Java8
PPTX
Java8lambda
PPTX
Java 8 streams
PDF
Functional aspects of java 8
PPTX
Kpi driven-java-development
PPTX
FUNctional Programming in Java 8
PDF
PDF
Java 8 by example!
PPTX
Functional programming
PDF
Functional Java 8 in everyday life
PDF
Java 8 - functional features
PPTX
What's new in Java 8
PPTX
Java 8 stream and c# 3.5
PPTX
Is java8 a true functional programming language
PPTX
Is java8a truefunctionallanguage
PDF
Java 8 Workshop
PDF
Java 8 Stream API. A different way to process collections.
PDF
Lambda.pdf
Gdg almaty. Функциональное программирование в Java 8
Java 8: more readable and flexible code
Exploring Streams and Lambdas in Java8
Java8lambda
Java 8 streams
Functional aspects of java 8
Kpi driven-java-development
FUNctional Programming in Java 8
Java 8 by example!
Functional programming
Functional Java 8 in everyday life
Java 8 - functional features
What's new in Java 8
Java 8 stream and c# 3.5
Is java8 a true functional programming language
Is java8a truefunctionallanguage
Java 8 Workshop
Java 8 Stream API. A different way to process collections.
Lambda.pdf

Fp java8

  • 1. 1 Ready for "Functional Programming" with Java 8 ? Yanai Franchi , Tikal
  • 2. 2 Agenda ● First-Class Functions ● FP with Streams ● Working Concurrency ● Demo
  • 4. 4 Our Inventory = List of Apples
  • 5. 5 Class Apple ● getColor() : String ● getWeight() : int
  • 8. 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
  • 9. 9 Now Heavy Ones , Please...
  • 10. 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
  • 11. 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; }
  • 12. 12
  • 13. 13 New Behavior Behavior Parameterization 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; }
  • 14. 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”
  • 15. 15
  • 16. 16 Java8 Lambda to Rescue filterApples(inventory, (Apple a) → {return a.getColor().equals(“green”)});
  • 17. 17 Sending Lambda Expression filterApples(inventory, (Apple a) → a.getColor().equals(“green”));
  • 18. 18 Making it Shorter filterApples(inventory, (a) → a.getColor().equals(“green”)); Apple Type is Inferred
  • 19. 19 ...Even Shorter filterApples(inventory, a → a.getColor().equals(“green”));
  • 20. 20 “Capture” Values (a.k.a “Closure”) String color = “green”; filterApples(inventory, a → a.getColor().equals(color)); Implicitly final
  • 21. 21
  • 22. 22 Anonymous Function that Can Be Passed Around filterApples(inventory, a → a.equals.getColor(“green”));
  • 23. 23 Switch to Method Reference filterApples(inventory,a → a.equals.getColor(“green”));
  • 24. 24 Switch to Method Reference filterApples(inventory,Apple::isGreen); filterApples(inventory,a → a.getColor().equals(“green”)); isGreen is declared in Apple
  • 27. 27 Lambda Type = Functional Interface Predicate<Apple> redApple = a → a.getColor().equals(“red”); Predicate is the type of our lambda
  • 28. 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
  • 29. 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
  • 30. 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!”));
  • 31. 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
  • 32. 32
  • 33. 33 Sorting with Anonymous Class inventory.sort(new Comparator<Apple>() { public int compare(Apple a1, Apple a2){ return a1.getWeight().compareTo(a2.getWeight()); } }); High Order Function
  • 34. 34 Sort with Lambda inventory.sort( (a1,a2) → a1.getWeight().compareTo(a2.getWeight()) );
  • 35. 35 Using “comparing” Method Comparator Interface Static method inventory.sort(comparing(Apple::getWeight)); Static method that accept Function and return Comparator
  • 37. 37 Dish Row 1 Row 2 Row 3 Row 4 0 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()
  • 38. 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
  • 39. 39
  • 40. 40 FP → Declarative Code List<String> fatDishes = menu.stream() .filter(d -> d.getCalories() > 300) .map(Dish::getName) .limit(3) .collect(toList());
  • 41. 41
  • 42. 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.
  • 43. 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)
  • 44. 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.
  • 45. 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.
  • 46. 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 egg filtering spaghetti mapping spaghetti Short Circuit: Stopped printing after 3 dishes
  • 47. 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)
  • 48. 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;
  • 49. 49 Grouping with FP - Declarative Map<Dish.Type, List<Dish>> groupDishesByTypeMap = menu.stream().collect(groupingBy(Dish::getType)); Collectors static method
  • 50. 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
  • 52. 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
  • 54. 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);
  • 55. 55 Total Calories in Menu – Imperative int i=0; for (Dish dish : menu) { i += dish.getCalories(); } Side Effect
  • 56. 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); }
  • 57. 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
  • 58. 58 Break Concurrency!!! :( Accumulator accumulator = new Accumulator(); menu.parallelStream() .map(Dish::getCalories) .forEach(accumulator::add) accumulator.getTotal(); Different result For the same input
  • 59. 59 Reducing Row 1 Row 2 Row 3 Row 4 0 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); =
  • 60. 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
  • 61. 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; }
  • 62. 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.
  • 66. 66 Persistent Tree 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 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)); }
  • 68. 68 Analyze Multiple Apache Logs ● Host, Time, Request, Response, Resp-Length 199.72.81.55 - - [02/Jul/1995:00:00:01 -0400] "GET /history/apollo/ HTTP/1.0" 200 6245 unicomp6.unicomp.net - - [02/Jul/1995:00:00:06 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985 199.120.110.21 - - [01/Jul/1995:00:00:09 -0400] "GET /shuttle/missions/sts-73/mission-sts-73.html HTTP/1.0" 200 4085 burger.letters.com - - [01/Jul/1995:00:00:11 -0400] "GET /shuttle/countdown/liftoff.html HTTP/1.0" 304 0 199.120.110.21 - - [01/Jul/1995:00:00:11 -0400] "GET /shuttle/missions/sts-73/sts-73-patch-small.gif HTTP/1.0" 200 4179 burger.letters.com - - [01/Jul/1995:00:00:12 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 304 0 burger.letters.com - - [01/Jul/1995:00:00:12 -0400] "GET /shuttle/countdown/video/livevideo.gif HTTP/1.0" 200 0 205.212.115.106 - - [01/Jul/1995:00:00:12 -0400] "GET /shuttle/countdown/countdown.html HTTP/1.0" 200 3985 d104.aa.net - - [01/Jul/1995:00:00:13 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985 129.94.144.152 - - [01/Jul/1995:00:00:13 -0400] "GET / HTTP/1.0" 200 7074 unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /shuttle/countdown/count.gif HTTP/1.0" 200 40310 unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 200 786 unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /images/KSC-logosmall.gif HTTP/1.0" 200 1204 d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /shuttle/countdown/count.gif HTTP/1.0" 200 40310 d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 200 786 d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /images/KSC-logosmall.gif HTTP/1.0" 200 1204 129.94.144.152 - - [01/Jul/1995:00:00:17 -0400] "GET /images/ksclogo-medium.gif HTTP/1.0" 304 0 199.120.110.21 - - [01/Jul/1995:00:00:17 -0400] "GET /images/launch-logo.gif HTTP/1.0" 200 1713 ppptky391.asahi-net.or.jp - - [01/Jul/1995:00:00:18 -0400] "GET /facts/about_ksc.html HTTP/1.0" 200 3977 ppptky391.asahi-net.or.jp - - [01/Jul/1995:00:00:19 -0400] "GET /images/launchpalms-small.gif HTTP/1.0" 200 11473 205.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 6245 unicomp6.unicomp.net - - [02/Jul/1995:00:00:06 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985 199.120.110.21 - - [01/Jul/1995:00:00:09 -0400] "GET /shuttle/missions/sts-73/mission-sts-73.html HTTP/1.0" 200 4085 burger.letters.com - - [01/Jul/1995:00:00:11 -0400] "GET /shuttle/countdown/liftoff.html HTTP/1.0" 304 0 199.120.110.21 - - [01/Jul/1995:00:00:11 -0400] "GET /shuttle/missions/sts-73/sts-73-patch-small.gif HTTP/1.0" 200 4179 burger.letters.com - - [01/Jul/1995:00:00:12 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 304 0 burger.letters.com - - [01/Jul/1995:00:00:12 -0400] "GET /shuttle/countdown/video/livevideo.gif HTTP/1.0" 200 0 205.212.115.106 - - [01/Jul/1995:00:00:12 -0400] "GET /shuttle/countdown/countdown.html HTTP/1.0" 200 3985 d104.aa.net - - [01/Jul/1995:00:00:13 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985 129.94.144.152 - - [01/Jul/1995:00:00:13 -0400] "GET / HTTP/1.0" 200 7074 unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /shuttle/countdown/count.gif HTTP/1.0" 200 40310 unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 200 786 unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /images/KSC-logosmall.gif HTTP/1.0" 200 1204 d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /shuttle/countdown/count.gif HTTP/1.0" 200 40310 d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 200 786 d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /images/KSC-logosmall.gif HTTP/1.0" 200 1204 129.94.144.152 - - [01/Jul/1995:00:00:17 -0400] "GET /images/ksclogo-medium.gif HTTP/1.0" 304 0 199.120.110.21 - - [01/Jul/1995:00:00:17 -0400] "GET /images/launch-logo.gif HTTP/1.0" 200 1713 ppptky391.asahi-net.or.jp - - [01/Jul/1995:00:00:18 -0400] "GET /facts/about_ksc.html HTTP/1.0" 200 3977 ppptky391.asahi-net.or.jp - - [01/Jul/1995:00:00:19 -0400] "GET /images/launchpalms-small.gif HTTP/1.0" 200 11473 205.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 6245 unicomp6.unicomp.net - - [02/Jul/1995:00:00:06 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985 199.120.110.21 - - [01/Jul/1995:00:00:09 -0400] "GET /shuttle/missions/sts-73/mission-sts-73.html HTTP/1.0" 200 4085 burger.letters.com - - [01/Jul/1995:00:00:11 -0400] "GET /shuttle/countdown/liftoff.html HTTP/1.0" 304 0 199.120.110.21 - - [01/Jul/1995:00:00:11 -0400] "GET /shuttle/missions/sts-73/sts-73-patch-small.gif HTTP/1.0" 200 4179 burger.letters.com - - [01/Jul/1995:00:00:12 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 304 0 burger.letters.com - - [01/Jul/1995:00:00:12 -0400] "GET /shuttle/countdown/video/livevideo.gif HTTP/1.0" 200 0 205.212.115.106 - - [01/Jul/1995:00:00:12 -0400] "GET /shuttle/countdown/countdown.html HTTP/1.0" 200 3985 d104.aa.net - - [01/Jul/1995:00:00:13 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985 129.94.144.152 - - [01/Jul/1995:00:00:13 -0400] "GET / HTTP/1.0" 200 7074 unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /shuttle/countdown/count.gif HTTP/1.0" 200 40310 unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 200 786 unicomp6.unicomp.net - - [01/Jul/1995:00:00:14 -0400] "GET /images/KSC-logosmall.gif HTTP/1.0" 200 1204 d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /shuttle/countdown/count.gif HTTP/1.0" 200 40310 d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /images/NASA-logosmall.gif HTTP/1.0" 200 786 d104.aa.net - - [01/Jul/1995:00:00:15 -0400] "GET /images/KSC-logosmall.gif HTTP/1.0" 200 1204 129.94.144.152 - - [01/Jul/1995:00:00:17 -0400] "GET /images/ksclogo-medium.gif HTTP/1.0" 304 0 199.120.110.21 - - [01/Jul/1995:00:00:17 -0400] "GET /images/launch-logo.gif HTTP/1.0" 200 1713 ppptky391.asahi-net.or.jp - - [01/Jul/1995:00:00:18 -0400] "GET /facts/about_ksc.html HTTP/1.0" 200 3977 ppptky391.asahi-net.or.jp - - [01/Jul/1995:00:00:19 -0400] "GET /images/launchpalms-small.gif HTTP/1.0" 200 11473 205.189.154.54 - - [01/Jul/1995:00:00:24 -0400] "GET /shuttle/countdown/ HTTP/1.0" 200 3985 … ...
  • 69. 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
  • 70. 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
  • 71. 71 Implementation private 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
  • 72. 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(); } }
  • 73. 73 Last Err Logs return streamLogs() .filter((le) -> le.getResponse() >= 500) .sorted(comparing(LogEntry::getDateTime).reversed()) .limit(5) .collect(toList());
  • 74. 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); }
  • 75. 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
  • 77. 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