Safe Harbor Statement The following is intended to outline our general product direcTon. It is intended for informaTon purposes only, and may not be incorporated into any contract. It is not a commitment to deliver any material, code, or funcTonality, and should not be relied upon in making purchasing decisions. The development, release, and Tming of any features or funcTonality described for Oracle’s products remains at the sole discreTon of Oracle. 2
Program Agenda Lambdas And Streams Primer Delaying ExecuTon Avoiding loops in Streams The Art of ReducTon Conclusions 1 2 3 4 5 Oracle ConfidenTal – Internal/Restricted/Highly Restricted 3
Lambda Expressions In JDK8 • Old style, anonymous inner classes • New style, using a Lambda expression 5 Simplified Parameterised Behaviour new Thread(new Runnable { public void run() { doSomeStuff(); } }).start(); new Thread(() -‐> doSomeStuff()).start();
Lambda Expressions • Lambda expressions represent anonymous funcTons – Same structure as a method • typed argument list, return type, set of thrown excepTons, and a body – Not associated with a class • We now have parameterised behaviour, not just values Some Details double highestScore = students. filter(Student s -‐> s.getGradYear() == 2011). map(Student s -‐> s.getScore()) max(); What How
Lambda Expression Types • Single-‐method interfaces are used extensively in Java – DefiniTon: a func%onal interface is an interface with one abstract method – Func%onal interfaces are idenTfied structurally – The type of a lambda expression will be a func%onal interface • Lambda expressions provide implementaTons of the abstract method interface Comparator<T> { boolean compare(T x, T y); } interface FileFilter { boolean accept(File x); } interface Runnable { void run(); } interface ActionListener { void actionPerformed(…); } interface Callable<T> { T call(); }
Local Variable Capture • Lambda expressions can refer to effec%vely final local variables from the surrounding scope – EffecTvely final: A variable that meets the requirements for final variables (i.e., assigned once), even if not explicitly declared final – Closures on values, not variables void expire(File root, long before) { root.listFiles(File p -‐> p.lastModified() <= before); }
What Does ‘this’ Mean In A Lambda • ‘this’ refers to the enclosing object, not the lambda itself • Think of ‘this’ as a final predefined local • Remember the Lambda is an anonymous func%on – It is not associated with a class – Therefore there can be no ‘this’ for the Lambda
Referencing Instance Variables Which are not final, or effecTvely final class DataProcessor { private int currentValue; public void process() { DataSet myData = myFactory.getDataSet(); dataSet.forEach(d -‐> d.use(currentValue++)); } }
Referencing Instance Variables The compiler helps us out class DataProcessor { private int currentValue; public void process() { DataSet myData = myFactory.getDataSet(); dataSet.forEach(d -‐> d.use(this.currentValue++); } } ‘this’ (which is effecTvely final) inserted by the compiler
Type Inference • The compiler can oeen infer parameter types in a lambda expression § Inferrence based on the target funcTonal interface’s method signature • Fully staTcally typed (no dynamic typing sneaking in) – More typing with less typing List<String> list = getList(); Collections.sort(list, (String x, String y) -‐> x.length() -‐ y.length()); Collections.sort(list, (x, y) -‐> x.length() -‐ y.length()); static T void sort(List<T> l, Comparator<? super T> c);
FuncTonal Interface DefiniTon • An interface • Must have only one abstract method – In JDK 7 this would mean only one method (like ActionListener) • JDK 8 introduced default methods – Adding mulTple inheritance of types to Java – These are, by definiTon, not abstract (they have an implementaTon) • JDK 8 also now allows interfaces to have staTc methods – Again, not abstract • @FunctionalInterface can be used to have the compiler check 13
Is This A FuncTonal Interface? 14 @FunctionalInterface public interface Runnable { public abstract void run(); } Yes. There is only one abstract method
Is This A FuncTonal Interface? 15 @FunctionalInterface public interface Predicate<T> { default Predicate<T> and(Predicate<? super T> p) {…}; default Predicate<T> negate() {…}; default Predicate<T> or(Predicate<? super T> p) {…}; static <T> Predicate<T> isEqual(Object target) {…}; boolean test(T t); } Yes. There is sTll only one abstract method
Is This A FuncTonal Interface? 16 @FunctionalInterface public interface Comparator { // Static and default methods elided int compare(T o1, T o2); boolean equals(Object obj); } The equals(Object) method is implicit from the Object class Therefore only one abstract method
Stream Overview • A stream pipeline consists of three types of things – A source – Zero or more intermediate operaTons – A terminal operaTon • Producing a result or a side-‐effect Pipeline int total = transactions.stream() .filter(t -‐> t.getBuyer().getCity().equals(“London”)) .mapToInt(Transaction::getPrice) .sum(); Source Intermediate operaTon Terminal operaTon
Stream Sources • From collecTons and arrays – Collection.stream() – Collection.parallelStream() – Arrays.stream(T array) or Stream.of() • StaTc factories – IntStream.range() – Files.walk() • Roll your own – java.util.Spliterator Many Ways To Create
Stream Terminal OperaTons • The pipeline is only evaluated when the terminal operaTon is called – All operaTons can execute sequenTally or in parallel – Intermediate operaTons can be merged • Avoiding mulTple redundant passes on data • Short-‐circuit operaTons (e.g. findFirst) • Lazy evaluaTon – Stream characterisTcs help idenTfy opTmisaTons • DISTINT stream passed to distinct() is a no-‐op
OpTonal Class • Terminal operaTons like min(), max(), etc do not return a direct result • Suppose the input Stream is empty? • Optional<T> – Container for an object reference (null, or real object) – Think of it like a Stream of 0 or 1 elements – use get(), ifPresent() and orElse() to access the stored reference – Can use in more complex ways: filter(), map(), etc • gpsMaybe.filter(r -‐> r.lastReading() < 2).ifPresent(GPSData::display); Helping To Eliminate the NullPointerException
Performance Impact For Logging • Heisenberg’s uncertainty principle • Semng log level to INFO sTll has a performance impact • Since Logger determines whether to log the message the parameter must be evaluated even when not used 22 logger.finest(getSomeStatusData()); Always executed
Supplier<T> • Represents a supplier of results • All relevant logging methods now have a version that takes a Supplier • Pass a descripTon of how to create the log message – Not the message • If the Logger doesn’t need the value it doesn’t invoke the Lambda • Can be used for other condiTonal acTviTes 23 logger.finest(() -‐> getSomeStatusData());
FuncTonal v. ImperaTve • For funcTonal programming you should not modify state • Java supports closures over values, not closures over variables • But state is really useful… 25
PrinTng And CounTng FuncTonal Interfaces 29 More Func?onal, But Not Pure Func?onal int count = functionalParameterMethodMap.get(c).stream() .mapToInt(m -‐> { int newMethod = 0; output.println(m); if (isNewMethod(c, m)) newMethod = 1; return newMethod }) .sum(); There is sTll state being modified in the Lambda
PrinTng And CounTng FuncTonal Interfaces 30 Even More Func?onal, But S?ll Not Pure Func?onal int count = functionalParameterMethodMap.get(nameOfClass) .stream() .peek(method -‐> output.println(method)) .mapToInt(m -‐> isNewMethod(nameOfClass, m) ? 1 : 0) .sum(); Strictly speaking prinTng is a side effect, which is not purely funcTonal
A Simple Problem • Find the length of the longest line in a file • Hint: BufferedReader has a new method, lines(), that returns a Stream 32 BufferedReader reader = ... reader.lines() .mapToInt(String::length) .max() .getAsInt();
Naïve Stream SoluTon • That works, so job done, right? • Not really. Big files will take a long Tme and a lot of resources • Must be a be7er approach 34 String longest = reader.lines(). sort((x, y) -‐> y.length() -‐ x.length()). findFirst(). get();
External IteraTon SoluTon • Simple, but inherently serial • Not thread safe due to mutable state 35 String longest = ""; while ((String s = reader.readLine()) != null) if (s.length() > longest.length()) longest = s;
Recursive Approach: Solving The Problem • No explicit loop, no mutable state, we’re all good now, right? • Unfortunately not -‐ larger data sets will generate an OOM excepTon 37 List<String> lines = new ArrayList<>(); while ((String s = reader.readLine()) != null) lines.add(s); String longest = findLongestString("", 0, lines);
A Be7er Stream SoluTon • The Stream API uses the well known filter-‐map-‐reduce pa7ern • For this problem we do not need to filter or map, just reduce Optional<T> reduce(BinaryOperator<T> accumulator) • BinaryOperator is a subclass of BiFunction, but all types are the same • R apply(T t, U u) or T apply(T x, T y) 38
A Be7er Stream SoluTon • The key is to find the right accumulator – The accumulator takes a parTal result and the next element, and returns a new parTal result – In essence it does the same as our recursive soluTon – But back to front – And without all the stack frames or List overhead 39
A Be7er Stream SoluTon • Use the recursive approach as an accululator for a reducTon 40 String longestLine = reader.lines() .reduce((x, y) -‐> { if (x.length() > y.length()) return x; return y; }) .get();
A Be7er Stream SoluTon • Use the recursive approach as an accululator for a reducTon 41 String longestLine = reader.lines() .reduce((x, y) -‐> { if (x.length() > y.length()) return x; return y; }) .get(); x in effect maintains state for us, by providing the parTal result, which is the longest string found so far
The Simplest Stream SoluTon • Use a specialised form of max() • One that takes a Comparator as a parameter • comparingInt() is a staTc method on Comparator – Comparator<T> comparingInt(ToIntFunction<? extends T> keyExtractor) 42 reader.lines() .max(comparingInt(String::length)) .get();
Conclusions • Lambdas provide a simple way to parameterise behaviour • The Stream API provides a funcTonal style of programming • Very powerful combinaTon • Does require developers to think differently • Avoid loops, even non-‐obvious ones! 44