java 8 and best practices

92
Jaffna Meetup 04 JAVA 8 Brought to you by WSO2 November 2016 1

Upload: buddhini-seneviratne

Post on 19-Feb-2017

31 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: Java 8 and Best Practices

Jaffna Meetup 04

JAVA 8

Brought to you by WSO2

November 2016

1

Page 2: Java 8 and Best Practices

Reference book• Based on training done by Sameera Jayasoma (Associate Director/Architect @ wso2)• I’ve used the book “Java 8 In Action” as the reference book.• You can get all the sample sources from here.

2

Page 3: Java 8 and Best Practices

What’s new in Java 8?• Lambda Expressions• Default Methods• Method references • Type Annotations and Pluggable Type Systems• Repeating Annotations• Method Parameter Reflection.• Security Enhancements.• Streams/Parallel Streams• Optional • Nashorn • New Date and Time API• No more permanent Generation

3

Page 4: Java 8 and Best Practices

Why Java 8?

• More concise code• Simpler use of multicore processors• No official support for previous versions

4

Page 5: Java 8 and Best Practices

Memory management

5

-XX:MetaspaceSize

Page 6: Java 8 and Best Practices

Functions as Values in Java 8

• Java 8 now treats Functions, or in other words methods as first-class citizens.

• Java used to treat primitives and object as first-class citizens.

• This allows us to pass methods around at runtime. This proved to be very useful in other functional languages.

6

Page 7: Java 8 and Best Practices

Behaviour Parameterization

• Is a software development pattern• easy to handle software requirement changes.

• Allows a block of code to be executed later• You can pass this block of code as an argument to a method.

• Now this method’s behaviour depends on the behaviour of this block of code.

• This is called behaviour parameterization.

• Consider a pet store. How do you filter out all the cats from the pet store?

7

Page 8: Java 8 and Best Practices

Attempt 1: Filtering Cats• A first solution would look like following.

8

public static List<Pet> filterCats(List<Pet> petList) {

List<Pet> catList = new ArrayList<>();

for (Pet pet : petList) {

if ("cat".equals(pet.getCategory())) {

catList.add(pet);

}

}

return catList;

}

• But now you need to filter pets who are more than 12 months older?

• What would you do?• Introduce another duplicate method?

Page 9: Java 8 and Best Practices

Attempt 2: Parameterizing the age• You can parameterize the age and be more flexible to such

changes

9

public static List<Pet> filterPetsByAge(List<Pet> petList, int age) {

List<Pet> filteredPetList = new ArrayList<>();

for (Pet pet : petList) {

if (pet.getAgeMonths() > age) {

filteredPetList.add(pet);

}

}

return filteredPetList;

}• You can now call this method as follows.

List<Pet> petList01 = filterPetsByAge(petList, 12);

List<Pet> petList02 = filterPetsByAge(petList, 24);

Page 10: Java 8 and Best Practices

Attempt 3: Filtering with every attribute

• Ugly attempt of merging all attributes appears as follows.

10

public static List<Pet> filterPets(List<Pet> petList, String category, int age,

boolean flag) {

List<Pet> filteredPetList = new ArrayList<>();

for (Pet pet : petList) {

if ((flag && pet.getCategory().equals(category)) ||

(!flag && pet.getAgeMonths() > age)) {

filteredPetList.add(pet);

}

}

return filteredPetList;

}• You can now call this method as follows. This is extremely bad!!! List<Pet> petList01 = filterPets(petList, "cat", 0, true);

List<Pet> petList02 = filterPets(petList, "", 12, false);

Page 11: Java 8 and Best Practices

Behaviour Parameterization... • How can we model the filtering criteria properly to cope with

the changing requirements?• This criteria returns a boolean for the given list of pets.

• What if we define an interface to model the selection criteria.

11

public interface PetFilter {

boolean test(Pet pet);

}

• Now we can easily come up with multiple implementations as follows. public class OlderPetFilter implements PetFilter{

@Override

public boolean filter(Pet pet) {

return pet.getAgeMonths() > 12;

}

}

Page 12: Java 8 and Best Practices

Attempt 4: Filtering by abstract criteria

• Modified filter method, which uses a PetFilter,

12

public static List<Pet> filterPets(List<Pet> petList, PetFilter filter) {

List<Pet> filteredPetList = new ArrayList<>();

for (Pet pet : petList) {

if (filter.test(pet)) {

filteredPetList.add(pet);

}

}

return filteredPetList;

}

• Now the behaviour of the filterPets method depends on the code you pass to it via PetFilter object.• I.e. We’ve parameterized the behaviour of the filterPets

method.

Page 13: Java 8 and Best Practices

Tackling verbosity• Consider the following PetFilter. Here we are interested in

only the return statement. All other lines are boilerplate code which contribute to verbosity.

13

public class OlderPetFilter implements PetFilter{

@Override

public boolean filter(Pet pet) {

return pet.getAgeMonths() > 12;

}

}

• When you want to pass new behaviour to filterPets method, you’re forced to declare classes that implement the PetFilter interface.

• This is unnecessary overhead; can we do better?

Page 14: Java 8 and Best Practices

Attempt 5: Using an anonymous class• Anonymous classes allow you to declare and instantiate a

class at the same time.• Don’t have a name. • Allow you to create ad-hoc implementations.

• Parameterizing the behaviour of the method filterPets directly inline.

14

List<Pet> filteredPetList = filterPets(petList, new PetFilter() {

@Override

public boolean test(Pet pet) {

return "cat".equals(pet.getCategory());

}

});

• Anonymous classes are still not good enough,• first, they tend to be very bulky because they take a lot of

space,• second, many programmers find them confusing to use.

Page 15: Java 8 and Best Practices

Attempt 6: Using a lambda expression• The previous code can be rewritten as follows in Java 8

using a lambda expression: List<Pet> filteredPetList = filterPets(petList, (Pet pet) -> "cat".equals(pet.getCategory()));

• This codes looks a lot cleaner than the previous attempt

15

Page 16: Java 8 and Best Practices

Sorting with a Comparator

Using an anonymous class,

16

petList.sort(new Comparator<Pet>() {

@Override

public int compare(Pet p1, Pet p2) {

return p1.getAgeMonths() - p2.getAgeMonths();

}

});

With lambda expression, petList.sort((Pet p1, Pet p2) -> p1.getAgeMonths() - p2.getAgeMonths());

Page 17: Java 8 and Best Practices

Lambda Expressions

17

Page 18: Java 8 and Best Practices

Lambdas in a nutshell

• Anonymous • We say anonymous because it doesn’t have an explicit name like a method would

normally have: less to write and think about!• Function

• We say function because lambda isn’t associated with a particular class like a method is. But like a method, a lambda has a list of parameters, a boy, a return type, and a possible list of exceptions that can be thrown.

• Passed around• A lambda expression can be passed as argument to a method or stored in a variable.

• Concise • You don’t need to write a lot of boilerplate like you do for anonymous classes.

18

(Pet p1, Pet p2) -> p1.getAgeMonths() - p2.getAgeMonths();

Lambda parameters

Lambda body

Arrow

Page 19: Java 8 and Best Practices

Lambda expressions

19

Before: petList.sort(new Comparator<Pet>() {

@Override

public int compare(Pet p1, Pet p2) {

return p1.getAgeMonths() - p2.getAgeMonths();

}

});

After (with lambda expressions) petList.sort((Pet p1, Pet p2) -> p1.getAgeMonths() - p2.getAgeMonths());

Page 20: Java 8 and Best Practices

Lambda expressions...

20

This lambda expression has three parts,

1. A list of parameters In this case it mirrors the parameters of the compare method of a Comparator.

2. An arrow The arrow -> separates the list of parameters from the body of the

lambda.

3. The body of the lambda Compare two Pets using their age in months. The expression is considered the lambda’s return value.

Page 21: Java 8 and Best Practices

Valid lambda expressions in Java 8

21

// 1) This expression has one parameter of type String and returns an int. The lambda doesn't have a return statement here because the return

is implied

(String s) -> s.length();

// 2) This expression has one parameter of type Pet and returns a boolean.

(Pet p) -> p.getAgeMonths() > 15;

// 3) This expression has two parameters of type int with no return (void return). Note that lambda expressions can contain multiple

statements, in this case two.

(int x, int y) -> {

System.out.println("Result:");

System.out.println(x + y);

};

// 4) This expression has no parameter and return an int.

() -> 42;

// 5) This expression has two parameters of type Pet and returns an int.

(Pet p1, Pet p2) -> p1.getAgeMonths() > p2.getAgeMonths();

Page 22: Java 8 and Best Practices

Syntax of a lambda

The basic syntax of a lambda is either,

(parameters) -> expression

or (note the curly braces for statements)

(parameters) -> { statements;}

22

Page 23: Java 8 and Best Practices

Examples of lambdas

23

Use case Examples of lambdas

A boolean expression (List<String> list) -> list.isEmpty()

Creating objects () -> new Pet(“cat”, “13”)

Consuming from an object (Pet p) -> { System.out.println(p.getAgeMonths());}

Select/extract from an object (String s) -> s.length()

Combine two values (int a, int b) -> a*b

Compare two values (Pet p1, Pet p2) -> p1.getAgeMonths() - p2.getAgeMonths()

Page 24: Java 8 and Best Practices

Where and how to use lambdas

• Here we can assign a lambda to a variable and also we can pass a lambda to the filter method.

• So where exactly can you use lambdas?

24

Predicate<Pet> petPredicate = (Pet pet) -> "cat".equals(pet.getCategory());

List<Pet> filteredPets = filter(petList, petPredicate);

• You can use a lambda in the context of a functional interface.

• Here filter method expects a Predicate<T>, which is a functional interface.

• So what are functional interfaces?

Page 25: Java 8 and Best Practices

Functional interface• A functional interface is an interface that specifies exactly

one abstract method.

25

// java.util.Comparator

public interface Comparator<T> {

int compare(T o1, T 02);

}

// java.util.Runnable

public interface Runnable {

void run();

}

// java.util.Callable

public interface Callable<V> {

V call();

}

• Lambda expressions let you provide the implementation of the abstract method of functional interface directly inline.

• Expression can be treated as an instance of a functional interface.

Page 26: Java 8 and Best Practices

@FunctionalInterface

• Function interfaces in the new Java API are annotated with @FunctionalInterface.

• This new annotation is indicates that the interface is intended to be a functional interface.• Compiler will throw error if the annotated interface is not a

functional interface.• This annotation is not mandatory, but it’s a good practise to

use it.

26

Page 27: Java 8 and Best Practices

The execute around pattern• A recurrent pattern in resource processing is to open a

resource, do some processing, and then close the resource. • The setup and cleanup phases are always similar and

surround the important code doing the processing• This is called the execute around pattern.

27

public static String processFile() throws IOException {

try (BufferedReader br = new BufferedReader(new FileReader("pets.txt"))) {

return br.readLine();

}

}

Page 28: Java 8 and Best Practices

Type inference...

• Now you can omit the types of the parameters from a lambda expression

28

Comparator<Pet> p = (Pet p1, Pet p2) -> p1.getAgeMonths() - p2.getAgeMonths();

Comparator<Pet> p = (p1, p2) -> p1.getAgeMonths() - p2.getAgeMonths();

• With type inference

Page 29: Java 8 and Best Practices

29

Streams API

Page 30: Java 8 and Best Practices

Java Collections API - Limitations• Collections is the most heavily used API in Java.• Allows you to group and process data.• Consider a collection of Pet objects in the PetStore sample.

• How would you group a list of Pet objects by category?• How would you find the oldest Pet in the PetStore?• How would you find the list of Cats who are older than 15

months.• Collections API is far from perfect to manipulate data and achieve

above requirements. • If you are to implement above requirements using Collections API,

then you need use iterators.• It’s very hard to leverage multicore architectures with iterators

30

Page 31: Java 8 and Best Practices

Imperative Style Programming

• Saying how to do something,• In terms of sequences of actions to be taken.

• Mutability• Too many moving parts• Hard make the code concurrent.

31

Page 32: Java 8 and Best Practices

Declarative Style Programming• Focus not on how, but what to do,

• Expressive• Concise• Immutability

• Most databases let you specify database operations declaratively.• E.g. consider the following SQL query

32

SELECT category, ageInMonths FROM Pets WHERE ageInMonths > 15

• Here you don’t specify how to filter data, instead you express only you what you expect.

• Can’t do we something similar with Collections?

Page 33: Java 8 and Best Practices

Streams

• Streams are an update to Java API that lets you manipulate collections of data in a declarative way.• You express a query rather than code

• Stream can be processed in parallel transparently• You don’t need to write any multithreaded code.

33

Page 34: Java 8 and Best Practices

Java 7

34

// 1) Filter the elements using and accumulator

List<Pet> youngPetList = new ArrayList<>();

for (Pet pet : petList) {

if (pet.getAgeMonths() < 15) {

youngPetList.add(pet);

}

}

// 2) Sort the pets with an anonymous class.

Collections.sort(youngPetList, new Comparator<Pet>() {

@Override

public int compare(Pet pet1, Pet pet2) {

return Integer.compare(pet1.getAgeMonths(), pet2.getAgeMonths());

}

});

// 3) Process the sorted list to select the names of dishes

List<String> youngPetNames = new ArrayList<>();

for (Pet pet : youngPetList) {

youngPetNames.add(pet.getName());

}

Page 35: Java 8 and Best Practices

Java 8

35

List<String> youngPetNames =

petList.stream()

.filter(pet -> pet.getAgeMonths() < 15)

.sorted(Comparator.comparing(Pet::getAgeMonths))

.map(pet -> pet.getName())

.collect(Collectors.toList());

// This exploits a multicore architecture and execute this code in parallel.

List<String> youngPetNames =

petList.parallelStream()

.filter(pet -> pet.getAgeMonths() < 15)

.sorted(Comparator.comparing(Pet::getAgeMonths))

.map(pet -> pet.getName())

.collect(Collectors.toList());

• This code is written in a declarative way. You specify what you want to achieve as opposed to specifying how to implement an operation.

• You chain together several building-block operations to express a complicated data processing pipeline while keeping your code readable and its intent is clear.

Page 36: Java 8 and Best Practices

What is a Stream?

• A sequence of elements from a source that supports data processing operations.

• Sequence of elements - Like a collection, a stream provides an interface to a sequenced set of values of a specific type.

• Source - Streams consume from a data-provisioning source such as collections, arrays, or I/O resources.

• Data processing operations - Supports database-like operations and common operations from functional programming languages. • e.g. filter, map, reduce, find, match, sort etc.

36

Page 37: Java 8 and Best Practices

Streams sample

37

List<String> youngPetNames =

//Get a stream from the pet list.

petList.stream()

// Filter pets who not older than 15 months.

.filter(pet -> pet.getAgeMonths() < 15)

// Sort the pets using their age.

.sorted(Comparator.comparing(Pet::getAgeMonths))

// Get the names of the pets.

.map(pet -> pet.getName())

// Select only the first three.

.limit(3)

// Store the resutls in another list.

.collect(Collectors.toList());

Page 38: Java 8 and Best Practices

Streams sample...

38

Page 39: Java 8 and Best Practices

Streams vs Collections

39

Page 40: Java 8 and Best Practices

Stream operations...

40

Page 41: Java 8 and Best Practices

41

Working with streamsFiltering and slicing

Page 42: Java 8 and Best Practices

Filtering with a predicate• filter method takes a Predicate (a function returning a boolean) and

returns a stream including all the elements that match the predicate.

• E.g. You can create a vegetarian menu by filtering all the vegetarian dishes from a menu.

42

List<Dish> vegetarianMenu =

menu.stream()

.filter(Dish::isVegetarian)

.collect(Collectors.toList());

Page 43: Java 8 and Best Practices

Filtering unique elements

43

• distinct method returns a stream with unique elements.• According to the implementations of the hashCode and equals

methods of the objects.• Consider the following example which filters all even numbers from

a list and makes sure that there are no duplicates.

Page 44: Java 8 and Best Practices

Filtering unique elements...

44

List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);

numbers.stream()

.filter(i -> i % 2 == 0)

.distinct()

.forEach(System.out::println);

Page 45: Java 8 and Best Practices

Truncating a stream

45

• limit(n) method returns another stream that’s no longer than a given size.• If the stream is ordered, the first elements are returned up to a

maximum of n. • Note that limit also works on unordered streams (e.g. if the

source is a Set). • You shouldn’t assume any order on the result in this case.

• How would you create a List by selecting the first three dishes that have more than 300 calories?

Page 46: Java 8 and Best Practices

Truncating a stream...

46

List<Dish> dishes =

menu.stream()

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

.limit(3)

.collect(Collectors.toList());

Page 47: Java 8 and Best Practices

Skipping elements

47

• skip(n) method returns another stream that discards the first n elements. • If the stream has fewer elements than n, then an empty stream

is returned.• For example, the following code skips the first two dishes that have

more than 300 calories and the return the rest.menu.stream()

.filter(dish ->

dish.getCalories() > 300)

.skip(2)

.collect(Collectors.toList());

Page 48: Java 8 and Best Practices

48

Working with streamsMapping

Page 49: Java 8 and Best Practices

Applying function to each elements

49

• map method takes a Function as an argument. This Function is applied to each element, mapping it to new element.

• For example, in the following code you pass a method reference Dish::getName to the map method to extract the names of the dishes in the stream.

• Because the method getName returns a String, the stream outputted by the map method is of type Stream<String>

List<String> dishNames =

menu.stream()

.map(Dish::getName)

.collect(Collectors.toList());

Page 50: Java 8 and Best Practices

50

Working with streamsBuilding streams

Page 51: Java 8 and Best Practices

Building streams

51

• Streams from values. Stream<String> stream = Stream.of("Java 8 ", "Lambdas ", "In ", "Action");

stream.map(String::toUpperCase).forEach(System.out::println);

// You can get an empty stream using the empty method as follow:

Stream<String> emptyStream = Stream.empty();

• Streams from arrays. int[] numbers = {2, 3, 5, 7, 11, 13};

int sum = Arrays.stream(numbers).sum();

Page 52: Java 8 and Best Practices

Building streams..

52

• Streams from files.

long uniqueWords = 0;

// Streams are autoclosable.

try (Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset())) {

uniqueWords = lines

// Generate a stream of words

.flatMap(line -> Arrays.stream(line.split(" ")))

// Remove duplicates

.distinct()

// Count the number of unique words

.count();

} catch (IOException e) {

// Deal with the exception if one occurs when opening the file.

}

Page 53: Java 8 and Best Practices

53

Default methods

Page 54: Java 8 and Best Practices

Evolving APIs• A Java interface groups related methods together into a contract

• Any class which implements this interface must provide an implementation for each method defined by the interface.

• This causes a problem when library designers need to update an interface to add a new method.

• Java 8 designers faced the exact same issue while introducing Streams API, but somehow they have introduced many new methods to existing APIs. For example,

• Collection.streamI()• Collection.parallelStream()• List.sort()

54

Page 55: Java 8 and Best Practices

Evolving APIs...

55

API Version 01

• Consider the following Interface public interface Resizable {

int getWidth();

int getHeight();

int setWidth(int width);

int setHeight(int height);

int setAbsoluteSize(int width, int height);

}• Assume you are a library designer and you’ve included the above API in one of your libraries.

• Also assume there are external developers who have introduced their own implementations based on this API.

• Now what if you want to introduce another method to this interface.

Page 56: Java 8 and Best Practices

Evolving APIs...

56

API Version 02

• Consider the following updated Interface public interface Resizable {

int getWidth();

int getHeight();

int setWidth(int width);

int setHeight(int height);

int setAbsoluteSize(int width, int height);

int setRelativeSize(int wFactor, int hFactor);

}

• This change creates few problems for existing API users.

Page 57: Java 8 and Best Practices

Default methods

57

• A new feature added in Java 8 to help evolve APIs in a compatible way.

• An interface can now contain method signatures for which an implementing class doesn’t provide an implementation.

• The implementation of a default method should there in the interface itself default void setRelativeSize(int wFactor, int hFactor) {

setAbsoluteSize(getWidth() / wFactor, getHeight() / hFactor);

}

• Default methods are used extensively in the Java 8 API.

Page 58: Java 8 and Best Practices

Abstract classes vs. interfaces in Java 8

58

• What’s the difference between an abstract class and an interface in Java 8? They both can contain abstract methods and methods with a body.

1. A class can extend only from one abstract class, but a class can implement multiple interfaces.

2. An abstract class can enforce a common state through instance variables (fields). An interface cannot have instance variables.

Page 59: Java 8 and Best Practices

59

Default methodsUsage patterns for default methods

Page 60: Java 8 and Best Practices

Optional methods

60

• There are methods in interfaces that we usually don’t implement, we always leave them empty.

• When you design an API, now you can provide a default implementation for such methods.• So the concrete classes don’t need to explicitly provide

an empty implementation.• This reduces boilerplate code.

Page 61: Java 8 and Best Practices

Resolution rules

61

• What if a class implements two interfaces that have the same default method signature? Which method is the class allowed to use?

• Consider the following example.

Page 62: Java 8 and Best Practices

Resolution rules...

62

public interface A {

default void hello() {

System.out.println("Hello from A");

}

}

public interface B {

default void hello() {

System.out.println("Hello from B");

}

}

public class C implements B, A {

public static void main(String[] args) {

// Which one gets printed

new C().hello();

}

}

Page 63: Java 8 and Best Practices

Three resolution rules to know

63

• There are three rules to follow when a class inherits a method with the same signature from multiple places

1. Classes always win. A method declaration in the class or a superclass takes priority over and default method declaration.

2. Otherwise, sub-interfaces win: the method with the same signature in the most specific default-providing interface is selected.

3. Finally, if the choice is still ambiguous, the class inheriting from multiple interfaces has to explicitly select which default method implementation to use by overriding it and calling the desired method explicitly.

Page 64: Java 8 and Best Practices

64

Parallel data processing

Fork/join framework Parallel streams Spliterator

Page 65: Java 8 and Best Practices

Fork/join framework• An implementation of the ExecutorService interface• Designed to recursively split a parallelizable task into smaller tasks, and

then combine the results of each subtasks.• ExecutorService distributes those subtasks to worker threads in the

ForkJoinPool (thread pool).• These subtasks need to implement RecursiveTasks<R> interface

• R is the type of the result produced by the tasks.• RecursiveAction if the task returns no result.

• You only need to implement the following abstract method to define a RecursiveTask

65

protected abstract R compute();

Page 66: Java 8 and Best Practices

Fork/join framework...

66

Page 67: Java 8 and Best Practices

Fork/join framework - sample

67

/**

* Extend {@code RecursiveTask} to create a task usable with the fork/join framework.

* Executing a parallel sum using the fork/join framework.

*/

public class ForkJoinSumCalculator extends RecursiveTask<Long> {

// The size of the array under which this task is no longer split into subtasks.

public static final long THRESHOLD = 10_000;

// The array of numbers to be summed.

private final long[] numbers;

// The initial and final positions of the portion of the array processed by this subtask.

private final int start;

private final int end;

// Public constructor used to create the main task

public ForkJoinSumCalculator(long[] numbers) {

this(numbers, 0, numbers.length);

}

// Private constructor used to recursively create subtasks of the main task.

private ForkJoinSumCalculator(long[] numbers, int start, int end) {

this.numbers = numbers;

this.start = start;

this.end = end;

}

Page 68: Java 8 and Best Practices

Fork/join framework - sample

68

protected Long compute() {

// The size of the portion of the array summed by this task.

int length = end - start;

if (length <= THRESHOLD) {

// If the size if less than or equal to the threshold, compute the result sequentially.

return computeSequentially();

}

// Create a subtask to sum the first half of the array.

ForkJoinSumCalculator leftTask = new ForkJoinSumCalculator(numbers, start, start + length / 2);

// Asynchronously execute the newly created subtask using the another

thread of the ForkJoinPool

leftTask.fork();

// Create a subtask to the sum the second half of the array.

ForkJoinSumCalculator rightTask = new ForkJoinSumCalculator(numbers, start + length / 2, end);

// Execute this second subtask synchronously, potentially allowing further recursive splits.

Long rightResult = rightTask.compute();

// Read the result of the first subtask or wait for it if it isn't ready.

Long leftResult = leftTask.join();

// The result of this task is the combination of the two subtasks.

return leftResult + rightResult;

}

Page 69: Java 8 and Best Practices

Fork/join framework - sample

69

// Simple algorithm calculating the result of a subtask when it's no longer divisible.

private long computeSequentially() {

long sum = 0;

for (int i = start; i < end; i++) {

sum += numbers[i];

}

return sum;

}

• Running the ForkJoinSumCalculator. public static long forkJoinSum(long n) {

long[] numbers = LongStream.rangeClosed(1, n).toArray();

ForkJoinTask<Long> task = new ForkJoinSumCalculator(numbers);

return new ForkJoinPool().invoke(task);

}

Page 70: Java 8 and Best Practices

Fork/join algorithm

70

Page 71: Java 8 and Best Practices

Best practises for using the fork/join framework

71

• Invoke the join method on a task blocks the caller until the result produced by that task is ready.

• The invoke method of a ForkJoinPool shouldn’t be used from within a RecursiveTask.

• Calling the fork method on a subtask is the way to schedule it on the ForkJoinPool.

Page 72: Java 8 and Best Practices

ForkJoinPool

72

• As a best practise, don’t create ForkJoinPool more than once in your application.

• Create is using the default no-argument constructor • This allows the pool to use all the processors available to

the JVM. • Calculates the number of threads based on the available

processors.

Page 73: Java 8 and Best Practices

73

• Allows you to execute operations in parallel on a collection of data without much effort.

• Allows you to declaratively turn a sequential stream into a parallel one. • Uses the fork/join framework under the hood.• A parallel stream is a stream that splits its elements into multiple chunks,

processing each chunks with a different thread.• This should keep all your cores busy by equally partitioning chunks

• Suppose you need to write a method accepting a number n as an argument and returning the sum of all the numbers from 1 to the given amount. Let’s optimize this method step by step.

Parallel streams

Page 74: Java 8 and Best Practices

74

• Using traditional java style.

Parallel streams...

public static long iterativeSum(long n) {

long result = 0;

for (long i = 0; i <= n; i++) {

result += i;

}

return result;

}

//Result

Iterative sum done in: 3 msecs

Page 75: Java 8 and Best Practices

75

• Using infinite stream of numbers, limiting it to the passed number, and then reduce the resulting stream with a BinaryOperator thats just sums two numbers

Parallel streams...

public static long rangedSum(long n) {

return LongStream.rangeClosed(1, n)

.reduce(0L, Long::sum);

}

//Result

Sequential sum done in: 4 msecs

• Previous one runs faster because its works at a much lower level and, more important, doesn’t need to perform any boxing or unboxing for primitive values

Page 76: Java 8 and Best Practices

76

Parallel streams...

Page 77: Java 8 and Best Practices

77

• Turning the sequential stream into a parallel one.

Parallel streams...

public static long parallelRangedSum(long n) {

return LongStream.rangeClosed(1, n)

.parallel()

.reduce(0L, Long::sum);

}

//Result

Sequential sum done in: 1 msecs

• Here the difference is that the stream is internally divided into multiple chunks. Reduction operation can work on the various chunks independently and in parallel.

Page 78: Java 8 and Best Practices

78

Parallel streams...

Page 79: Java 8 and Best Practices

79

• How can we configure the thread pool size here?• Parallel streams internally use the default ForkJoinPool

• This pool has as many threads as you have processors, as returned by Runtime.getRuntime().availableProcessors().

• You can change the size of the ForkJoinPool using the following system property.

Configuring the thread pool

System.setProperty(“java.util.concurrent.ForkJoinPool.common.parallelism”, 12)

• This is a global setting, so it will affect all the parallel streams in your code.• The default value of this pool is equal to the number of processors on your

machine and it is a meaningful default. • Don’t change it unless you have a very good reason for doing so.

Page 80: Java 8 and Best Practices

80

• If in doubt, measure. Turning a sequential stream into a parallel one is trivial but not always the right thing to do.

• Watch out for boxing. Automatic boxing and unboxing operations can dramatically hurt performance.

• Some operations naturally perform worse on a parallel stream than on a sequential stream. For example limit and findFirst vs. findAny

• Consider the total computational cost of the pipeline of operations performed by the stream. With N being the number of elements to be processed and Q the approximate cost of processing one of these elements through the stream pipeline, the product of N*Q gives a rough qualitative estimation of this cost. A higher value for Q implies a better chance of good performance when using a parallel stream.

Using parallel streams effectively

Page 81: Java 8 and Best Practices

81

• Code readability can be very subjective. • Improve code readability means ensuring your code is understandable

and maintainable by people beside you.• How Java 8 allows you to improve code readability?

• You can reduce the verbosity of your code, making it easier to understand.

• You can improve the intent of your code by using method references and the Streams API

• Following are three simple refactorings that use lambdas, method references , and streams

• Refactoring anonymous classes to lambda expressions• Refactoring lambda expressions to method references• Refactoring imperative-style data processing to streams.

Improving code readability

Page 82: Java 8 and Best Practices

82

• Try to convert anonymous classes implementing one single abstract method to lambda expressions.

• But sometime, you may get into issues• The meanings of this and super are different from

anonymous classes and lambda expressions. • Inside an anonymous class, this refers to the anonymous

class itself, but inside a lambda it refers to the enclosing class.

• Anonymous classes are allowed to shadow variables from the enclosing class. Lambda expressions can’t do that.

From anonymous classes to lambdas

Page 83: Java 8 and Best Practices

83

From anonymous classes to lambdas...

Page 84: Java 8 and Best Practices

84

• Converting an anonymous class to a lambda expression can make the resulting code ambiguous in the context of overloading.

• The type of the anonymous class is explicit at instantiation.• But, the type of the lambda depends on its context.

From anonymous classes to lambdas...

• You can now pass an anonymous class implementing Task without a problem.

Page 85: Java 8 and Best Practices

85

• But converting the previous anonymous class to a lambda results in an ambiguous method call, because both Runnable and Task are valid target types.

From anonymous classes to lambdas...

• You can solve the ambiguity by the providing an explicit cast.

Page 86: Java 8 and Best Practices

86

Best practises

List <String> myList = …;

if (myList.size > 0) { … }

if myList.isEmpty() { … }

Page 87: Java 8 and Best Practices

87

Best practises

Returning Empty Collections instead of Null

public static List<Pet> getAllPets(){List<Pet> lPets = new ArrayList()<Pet>();

................return lPets;

}

Page 88: Java 8 and Best Practices

88

Best practises

Avoid unnecessary Objects

public static List<Pet> getAllPets(){List<Pet> lPets = null;//if throw exception etc

................if (lPets == null) {

lPets = new ArrayList()<Pet>();}................return lPets;

}

//Slower InstantiationString bad = new String("Yet another string object");//Faster InstantiationString good = "Yet another string object";

Page 89: Java 8 and Best Practices

89

Best practisesDilemma between Array and ArrayList

• Arrays have fixed size but ArrayLists have variable sizes. Since the size of Array is fixed, the memory gets allocated at the time of declaration of Array type variable. Hence, Arrays are very fast. On the other hand, if we are not aware of the size of the data, then ArrayList is More data will lead to ArrayOutOfBoundException and less data will cause wastage of storage space.

• It is much easier to Add or Remove elements from ArrayList than Array• Array can be multi-dimensional but ArrayList can be only one dimension.

Page 90: Java 8 and Best Practices

90

Best practises

Difference between single quotes and double quotes

public static void main (String [] args) {System.out.println("H" + "I");System.out.println('H' + 'I');

}

Page 91: Java 8 and Best Practices

91

Best practises

Choice between Float and Double

45.123456…?

Most processors take nearly the same amount of processing time to perform operations on Float and Double. Double offers far more precision in the same amount of computation time.