Contenu connexe Similaire à JDK8 Lambdas and Streams: Changing The Way You Think When Developing Java (20) Plus de Simon Ritter (20) JDK8 Lambdas and Streams: Changing The Way You Think When Developing Java1. JDK8 Lambdas and Streams:
Changing The Way You Think
When Developing Java
Simon Ritter
Head of Java Technology Evangelism
Oracle Corp.
Twitter: @speakjava
Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
2. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Safe Harbor Statement
The following is intended to outline our general product direction. It is intended for
information purposes only, and may not be incorporated into any contract. It is not a
commitment to deliver any material, code, or functionality, and should not be relied upon
in making purchasing decisions. The development, release, and timing of any features or
functionality described for Oracle’s products remains at the sole discretion of Oracle.
2
3. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Lambda & Streams
Primer
4. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Lambda Expressions In JDK8
• Old style, anonymous inner classes
• New style, using a Lambda expression
4
Simplified Parameterised Behaviour
new Thread(new Runnable {
public void run() {
doSomeStuff();
}
}).start();
new Thread(() -> doSomeStuff()).start();
5. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Lambda Expressions
• Lambda expressions represent anonymous functions
– Same structure as a method
• typed argument list, return type, set of thrown exceptions, 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
6. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Lambda Expression Types
• Single-method interfaces are used extensively in Java
– Definition: a functional interface is an interface with one abstract method
– Functional interfaces are identified structurally
– The type of a lambda expression will be a functional interface
• Lambda expressions provide implementations 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(); }
7. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Local Variable Capture
• Lambda expressions can refer to effectively final local variables from the
surrounding scope
– Effectively 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);
}
8. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
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 function
– It is not associated with a class
– Therefore there can be no ‘this’ for the Lambda
9. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Referencing Instance Variables
Which are not final, or effectively final
class DataProcessor {
private int currentValue;
public void process() {
DataSet myData = myFactory.getDataSet();
dataSet.forEach(d -> d.use(currentValue++));
}
}
10. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
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 effectively final)
inserted by the compiler
11. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Type Inference
• The compiler can often infer parameter types in a lambda expression
Inferrence based on the target functional interface’s method signature
• Fully statically 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);
12. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Method References
• Method references let us reuse a method as a lambda expression
FileFilter x = File f -> f.canRead();
FileFilter x = File::canRead;
13. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Method References
13
Rules For Construction
Lambda
Method Ref
Lambda
Method Ref
Lambda
Method Ref
(args) -> ClassName.staticMethod(args)
(arg0, rest) -> arg0.instanceMethod(rest)
(args) -> expr.instanceMethod(args)
ClassName::staticMethod
ClassName::instanceMethod
expr::instanceMethod
instanceOf
14. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Constructor References
• Same concept as a method reference
– For the constructor
Factory<List<String>> f = ArrayList<String>::new;
Factory<List<String>> f = () -> return new ArrayList<String>();
Replace with
15. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Useful Methods That Can Use Lambdas
• Iterable.forEach(Consumer c)
List<String> myList = ...
myList.forEach(s -> System.out.println(s));
myList.forEach(System.out::println);
16. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Useful Methods That Can Use Lambdas
• Collection.removeIf(Predicate p)
List<String> myList = ...
myList.removeIf(s -> s.length() == 0)
17. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Useful Methods That Can Use Lambdas
• List.replaceAll(UnaryOperator o)
List<String> myList = ...
myList.replaceAll(s -> s.toUpperCase());
myList.replaceAll(String::toUpper);
18. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Useful Methods That Can Use Lambdas
• List.sort(Comparator c)
• Replaces Collections.sort(List l, Comparator c)
List<String> myList = ...
myList.sort((x, y) -> x.length() – y.length());
19. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Useful Methods That Can Use Lambdas
• Logger.finest(Supplier<String> msgSupplier)
• Overloads Logger.finest(String msg)
logger.finest(produceComplexMessage());
logger.finest(() -> produceComplexMessage());
20. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Functional Interface Definition
• 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 multiple inheritance of types to Java
– These are, by definition, not abstract (they have an implementation)
• JDK 8 also now allows interfaces to have static methods
– Again, not abstract
• @FunctionalInterface can be used to have the compiler check
20
21. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Is This A Functional Interface?
21
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
Yes. There is only
one abstract method
22. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Is This A Functional Interface?
22
@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 still only
one abstract method
23. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Is This A Functional Interface?
23
@FunctionalInterface
public interface Comparator {
// default and static 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
24. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Stream Overview
• A stream pipeline consists of three types of things
– A source
– Zero or more intermediate operations
– A terminal operation
• 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 operation
Terminal operation
25. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Stream Sources
• From collections and arrays
– Collection.stream()
– Collection.parallelStream()
– Arrays.stream(T array) or Stream.of()
• Static factories
– IntStream.range()
– Files.walk()
• Roll your own
– java.util.Spliterator
Many Ways To Create
26. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Maps and FlatMaps
Map Values in a Stream
Map
FlatMap
Input Stream
Input Stream
1-to-1 mapping
1-to-many mapping
Output Stream
Output Stream
27. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
The Optional Class
28. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Optional<T>
Reducing NullPointerException Occurrences
String direction = gpsData.getPosition().getLatitude().getDirection();
String direction = “UNKNOWN”;
if (gpsData != null) {
Position p = gpsData.getPosition();
if (p != null) {
Latitude latitude = p.getLatitude();
if (latitude != null)
direction = latitude.getDirection();
}
}
String direction = gpsData.getPosition().getLatitude().getDirection();
29. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Optional Class
• Terminal operations 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
30. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Optional ifPresent()
Do something when set
if (x != null) {
print(x);
}
opt.ifPresent(x -> print(x));
opt.ifPresent(this::print);
31. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Optional filter()
Reject certain values of the Optional
if (x != null && x.contains("a")) {
print(x);
}
opt.filter(x -> x.contains("a"))
.ifPresent(this::print);
32. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Optional map()
Transform value if present
if (x != null) {
String t = x.trim();
if (t.length() > 1)
print(t);
}
opt.map(String::trim)
.filter(t -> t.length() > 1)
.ifPresent(this::print);
33. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Optional flatMap()
Going deeper
public String findSimilar(String s)
Optional<String> tryFindSimilar(String s)
Optional<Optional<String>> bad = opt.map(this::tryFindSimilar);
Optional<String> similar = opt.flatMap(this::tryFindSimilar);
34. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Update Our GPS Code
class GPSData {
public Optional<Position> getPosition() { ... }
}
class Position {
public Optional<Latitude> getLatitude() { ... }
}
class Latitude {
public String getString() { ... }
}
35. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Update Our GPS Code
String direction = Optional.ofNullable(gpsData)
.flatMap(GPSData::getPosition)
.flatMap(Position::getLatitude)
.map(Latitude::getDirection)
.orElse(“None”);
36. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Advanced Lambdas And Streams
37. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Streams and Concurrency
38. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Serial And Parallel Streams
• Collection Stream sources
– stream()
– parallelStream()
• Stream can be made parallel or sequential at any point
– parallel()
– sequential()
• The last call wins
– Whole stream is either sequential or parallel
38
39. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Parallel Streams
• Implemented underneath using the fork-join framework
• Will default to as many threads for the pool as the OS reports processors
– Which may not be what you want
System.setProperty(
"java.util.concurrent.ForkJoinPool.common.parallelism",
"32767");
• Remember, parallel streams always need more work to process
– But they might finish it more quickly
39
40. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
When To Use Parallel Streams
• Data set size is important, as is the type of data structure
– ArrayList: GOOD
– HashSet, TreeSet: OK
– LinkedList: BAD
• Operations are also important
– Certain operations decompose to parallel tasks better than others
– filter() and map() are excellent
– sorted() and distinct() do not decompose well
40
Sadly, no simple answer
41. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
When To Use Parallel Streams
• N = size of the data set
• Q = Cost per element through the Stream pipeline
• N x Q = Total cost of pipeline operations
• The bigger N x Q is the better a parallel stream will perform
• It is easier to know N than Q, but Q can be estimated
• If in doubt, profile
41
Quantative Considerations
42. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Streams: Pitfalls For The Unwary
43. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Functional v. Imperative
• For functional programming you should not modify state
• Java supports closures over values, not closures over variables
• But state is really useful…
43
44. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Counting Methods That Return Streams
44
Still Thinking Imperatively
Set<String> sourceKeySet = streamReturningMethodMap.keySet();
LongAdder sourceCount = new LongAdder();
sourceKeySet.stream()
.forEach(c ->
sourceCount.add(streamReturningMethodMap.get(c).size()));
45. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Counting Methods That Return Streams
45
Functional Way
sourceKeySet.stream()
.mapToInt(c -> streamReturningMethodMap.get(c).size())
.sum();
46. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Printing And Counting Functional Interfaces
46
Still Thinking Imperatively
LongAdder newMethodCount = new LongAdder();
functionalParameterMethodMap.get(c).stream()
.forEach(m -> {
output.println(m);
if (isNewMethod(c, m))
newMethodCount.increment();
});
return newMethodCount.intValue();
47. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Printing And Counting Functional Interfaces
47
More Functional, But Not Pure Functional
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 still state being
modified in the Lambda
48. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Printing And Counting Functional Interfaces
48
Even More Functional, But Still Not Pure Functional
int count = functionalParameterMethodMap.get(nameOfClass)
.stream()
.peek(method -> output.println(method))
.mapToInt(m -> isNewMethod(nameOfClass, m) ? 1 : 0)
.sum();
Strictly speaking printing
is a side effect, which is
not purely functional
49. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
The Art Of Reduction
(Or The Need to Think Differently)
50. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
A Simple Problem
• Find the length of the longest line in a file
• Hint: BufferedReader has a new method, lines(), that returns a Stream
50
BufferedReader reader = ...
reader.lines()
.mapToInt(String::length)
.max()
.getAsInt();
51. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Another Simple Problem
• Find the length of the longest line in a file
51
52. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Naïve Stream Solution
• That works, so job done, right?
• Not really. Big files will take a long time and a lot of resources
• Must be a better approach
52
String longest = reader.lines().
sort((x, y) -> y.length() - x.length()).
findFirst().
get();
53. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
External Iteration Solution
• Simple, but inherently serial
• Not thread safe due to mutable state
53
String longest = "";
while ((String s = reader.readLine()) != null)
if (s.length() > longest.length())
longest = s;
54. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Recursive Approach: The Method
54
String findLongestString(String s, int index, List<String> l) {
if (index >= l.size())
return s;
if (index == l.size() - 1) {
if (s.length() > l.get(index).length())
return s;
return l.get(index);
}
String s2 = findLongestString(l.get(start), index + 1, l);
if (s.length() > s2.length())
return s;
return s2;
}
55. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
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 exception
55
List<String> lines = new ArrayList<>();
while ((String s = reader.readLine()) != null)
lines.add(s);
String longest = findLongestString("", 0, lines);
56. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
A Better Stream Solution
• The Stream API uses the well known filter-map-reduce pattern
• For this problem we do not need to filter or map, just reduce
Optional<T> reduce(BinaryOperator<T> accumulator)
• The key is to find the right accumulator
– The accumulator takes a partial result and the next element, and returns a new
partial result
– In essence it does the same as our recursive solution
– Without all the stack frames
• 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)
56
57. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
A Better Stream Solution
• Use the recursive approach as an accululator for a reduction
57
String longestLine = reader.lines()
.reduce((x, y) -> {
if (x.length() > y.length())
return x;
return y;
})
.get();
58. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
A Better Stream Solution
• Use the recursive approach as an accululator for a reduction
58
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 always holding the
longest string found so far
59. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
The Simplest Stream Solution
• Use a specialised form of max()
• One that takes a Comparator as a parameter
• comparingInt() is a static method on Comparator
– Comparator<T> comparingInt(ToIntFunction<? extends T> keyExtractor)
59
reader.lines()
.max(comparingInt(String::length))
.get();
60. Copyright © 2014, Oracle and/or its affiliates. All rights reserved.
Conclusions
• Java needs lambda statements
– Significant improvements in existing libraries are required
• Require a mechanism for interface evolution
– Solution: virtual extension methods
• Bulk operations on Collections
– Much simpler with Lambdas
• Java SE 8 evolves the language, libraries, and VM together
Notes de l'éditeur This is a Title Slide with Java FY15 Theme slide ideal for including the Java Theme with a brief title, subtitle and presenter information.
To customize this slide with your own picture:
Right-click the slide area and choose Format Background from the pop-up menu. From the Fill menu, click Picture and texture fill. Under Insert from: click File. Locate your new picture and click Insert.
To copy the Customized Background from Another Presentation on PC
Click New Slide from the Home tab's Slides group and select Reuse Slides.
Click Browse in the Reuse Slides panel and select Browse Files. Double-click the PowerPoint presentation that contains the background you wish to copy.
Check Keep Source Formatting and click the slide that contains the background you want.
Click the left-hand slide preview to which you wish to apply the new master layout.
Apply New Layout (Important): Right-click any selected slide, point to Layout, and click the slide containing the desired layout from the layout gallery.
Delete any unwanted slides or duplicates.
To copy the Customized Background from Another Presentation on Mac
Click New Slide from the Home tab's Slides group and select Insert Slides from Other Presentation…
Navigate to the PowerPoint presentation file that contains the background you wish to copy. Double-click or press Insert. This prompts the Slide Finder dialogue box.
Make sure Keep design of original slides is unchecked and click the slide(s) that contains the background you want. Hold Shift key to select multiple slides.
Click the left-hand slide preview to which you wish to apply the new master layout.
Apply New Layout (Important): Click Layout from the Home tab's Slides group, and click the slide containing the desired layout from the layout gallery.
Delete any unwanted slides or duplicates. This is a Safe Harbor Front slide, one of two Safe Harbor Statement slides included in this template.
One of the Safe Harbor slides must be used if your presentation covers material affected by Oracle’s Revenue Recognition Policy
To learn more about this policy, e-mail: Revrec-americasiebc_us@oracle.com
For internal communication, Safe Harbor Statements are not required. However, there is an applicable disclaimer (Exhibit E) that should be used, found in the Oracle Revenue Recognition Policy for Future Product Communications. Copy and paste this link into a web browser, to find out more information.
http://my.oracle.com/site/fin/gfo/GlobalProcesses/cnt452504.pdf
For all external communications such as press release, roadmaps, PowerPoint presentations, Safe Harbor Statements are required. You can refer to the link mentioned above to find out additional information/disclaimers required depending on your audience. We define a Lambda expression as an anonymous function (like a method, but because it is not associated with a class we call it a function). Like methods there are parameters, a body, a return type and even thrown exceptions.
What Lambda expressions really brings to Java is a simple way to parameterise behaviour. The sequence of methods we have here defines what we want to do, i.e. filter the stream, map its values and so on, but how this happens is defined by the Lambda expressions we pass as parameters. Erased function types are the worst of both worlds Stream is an interface, but in Java SE 8 we can now have static methods in interfaces, hence Stream.of()
Files.walk will walk a file tree from a given Path argument
Spliterator interface that represents an object for traversing or partitioning elements of a source This is a Title Slide with Java FY15 Theme slide ideal for including the Java Theme with a brief title, subtitle and presenter information.
To customize this slide with your own picture:
Right-click the slide area and choose Format Background from the pop-up menu. From the Fill menu, click Picture and texture fill. Under Insert from: click File. Locate your new picture and click Insert.
To copy the Customized Background from Another Presentation on PC
Click New Slide from the Home tab's Slides group and select Reuse Slides.
Click Browse in the Reuse Slides panel and select Browse Files. Double-click the PowerPoint presentation that contains the background you wish to copy.
Check Keep Source Formatting and click the slide that contains the background you want.
Click the left-hand slide preview to which you wish to apply the new master layout.
Apply New Layout (Important): Right-click any selected slide, point to Layout, and click the slide containing the desired layout from the layout gallery.
Delete any unwanted slides or duplicates.
To copy the Customized Background from Another Presentation on Mac
Click New Slide from the Home tab's Slides group and select Insert Slides from Other Presentation…
Navigate to the PowerPoint presentation file that contains the background you wish to copy. Double-click or press Insert. This prompts the Slide Finder dialogue box.
Make sure Keep design of original slides is unchecked and click the slide(s) that contains the background you want. Hold Shift key to select multiple slides.
Click the left-hand slide preview to which you wish to apply the new master layout.
Apply New Layout (Important): Click Layout from the Home tab's Slides group, and click the slide containing the desired layout from the layout gallery.
Delete any unwanted slides or duplicates.