Java’s Stream APIoosd192/wiki.files/PS5.pdf · class or interface, without having to actually...

24
Java’s Stream API

Transcript of Java’s Stream APIoosd192/wiki.files/PS5.pdf · class or interface, without having to actually...

Page 1: Java’s Stream APIoosd192/wiki.files/PS5.pdf · class or interface, without having to actually subclass a class. A drawback with anonymous classes is they can't have explicit constructors.

Java’s Stream API

Page 2: Java’s Stream APIoosd192/wiki.files/PS5.pdf · class or interface, without having to actually subclass a class. A drawback with anonymous classes is they can't have explicit constructors.

Introduction

Given a collection c and a function f, sometime we’d like to create

a new collection c’ such that for each element x in c we’ll have f(x)

in c’.

For instance, we have the collection called intStrings with the

following elements: (“1”, “2”, “3”, “4”), and the function

Integer::parseInt which takes a string and parse it into an integer.

Well, we could of course do the following:

List<String> intStrings = Arrays.asList("1", "2", "3", "4");

List<Integer> integers = new ArrayList<>();

for(int i=0; i<intStrings.size(); i++){

int parsed = Integer.parseInt(intStrings.get(i));

integers.add(parsed);

}

Page 3: Java’s Stream APIoosd192/wiki.files/PS5.pdf · class or interface, without having to actually subclass a class. A drawback with anonymous classes is they can't have explicit constructors.

Introduction

This code indeed works as intended and we get a new list, called

“integers” with the elements (1, 2, 3, 4).

However, as the amount of code and complexity of tasks scales up,

such programming style can make our code less understandable

and complex.

Instead, what if we could tell our collection to simply apply that

function on each of it’s elements?

Fortunately, we can do this, using the java Stream API!

List<String> intStrings = Arrays.asList("1", "2", "3", "4");

List<Integer> integers = intStrings.stream()

.map(Integer::parseInt)

.collect(Collectors.toList());

Page 4: Java’s Stream APIoosd192/wiki.files/PS5.pdf · class or interface, without having to actually subclass a class. A drawback with anonymous classes is they can't have explicit constructors.

Introduction

Functional programming is a programming paradigm – a style of

building the structure and elements of computer program.

Treats computation as the evaluation of mathematical functions.

Avoids changing-state and mutable data.

A different approach than the object oriented programming which

relies on the object’s state.

Page 5: Java’s Stream APIoosd192/wiki.files/PS5.pdf · class or interface, without having to actually subclass a class. A drawback with anonymous classes is they can't have explicit constructors.

Functional vs Imperative

Programming

Characteristic Imperative approach Functional approach

Programmer focus How to perform tasks

(algorithms) and how

to track changes in

state

What information is

desired and what

transformations are

required?

State changes Important Almost non-existent

Order of execution Important Low importance

Primary flow control Loops, conditionals

and function calls

Function calls,

including recursion

Primary manipulation

unit

Instances of structures

or classes

Functions as first-class

objects and data

collections

Page 6: Java’s Stream APIoosd192/wiki.files/PS5.pdf · class or interface, without having to actually subclass a class. A drawback with anonymous classes is they can't have explicit constructors.

Anonymous Class

Definition: An anonymous class is a class that have no name.

Allows declaring and instantiating a class at the same time.

An anonymous inner class can be useful when making an instance

of an object with certain “extras” such as overloading methods of a

class or interface, without having to actually subclass a class.

A drawback with anonymous classes is they can't have explicit

constructors. You can't pass them any parameters when they are

instantiated.

One of the key benefits of an anonymous class is encapsulation.

Page 7: Java’s Stream APIoosd192/wiki.files/PS5.pdf · class or interface, without having to actually subclass a class. A drawback with anonymous classes is they can't have explicit constructors.

Anonymous Class

For instance:

public interface

Printer {

void print();

}

public static void main(String[] args){

Printer p1 = new Printer() {

@Override

public void print() {

System.out.println("This is a message from p1");

}

};

Printer p2 = new Printer() {

@Override

public void print() {

System.out.println("This is a message from p2.");

}

};

}

Page 8: Java’s Stream APIoosd192/wiki.files/PS5.pdf · class or interface, without having to actually subclass a class. A drawback with anonymous classes is they can't have explicit constructors.

Lambda Expressions

Definition: A lambda expression is a block of code that is treated as

an object.

It can be passed as an argument to another function, or used for

constructing the return value of a function.

In java: lambda expressions are just a simpler way of instantiating an

anonymous class.

We use the following syntax to instantiate a lambda expression:

(<arg 1>, …, <arg n>) -> <returned expression>;

(<arg 1>, …, <arg n>) -> {

<expr 1>;

<expr n>;

return <expr>;

};

Page 9: Java’s Stream APIoosd192/wiki.files/PS5.pdf · class or interface, without having to actually subclass a class. A drawback with anonymous classes is they can't have explicit constructors.

Lambda Expressions

In the following example:

Adder is an interface with the method

‘perform’ that takes an Integer and returns an

Integer.

“add1” is an anonymous class that

implements adder.

“_add1” is an equivalent lambda expression.

We can call ‘add1.perform(x)’ which returns

x+1.

public interface Adder {

int perform(int x);

}

public static void main(String[] args){

Adder add1 = new Adder() {

@Override

public int perform(int x) {

return x+1;

}

};

Adder _add1 = x -> x+1;

}

int y = add1.perform(3);

Page 10: Java’s Stream APIoosd192/wiki.files/PS5.pdf · class or interface, without having to actually subclass a class. A drawback with anonymous classes is they can't have explicit constructors.

Functions as variables / parameters

Some languages allows passing function as parameters to another

functions, or instantiate functions as variables.

This is supported by some languages (including C, C++ and C#)

The famous Language-Integrated-Query (LINQ) of C# provides a

simple mechanism, very similar to java’s stream.

Both C and C++ allows the programmer to treat functions as

variables.

This mechanism works similarly in modern languages like python and

javascript.

Page 11: Java’s Stream APIoosd192/wiki.files/PS5.pdf · class or interface, without having to actually subclass a class. A drawback with anonymous classes is they can't have explicit constructors.

In Java

In java we don’t really pass functions as arguments.

Instead we use interfaces with a single method.

Anonymous classes with a single method can be turned into

lambda expressions.

It’s called Syntactic Sugar.

Java simulates its lambda expressions and functional interface in an

object-oriented style, as form of abstraction.

Page 12: Java’s Stream APIoosd192/wiki.files/PS5.pdf · class or interface, without having to actually subclass a class. A drawback with anonymous classes is they can't have explicit constructors.

In Java

Take a look at the previous example of anonymous class:

The anonymous ‘new Printer()…’ can be replaced with the following

equal lambda expression:

Hint: your Intellij will suggest by default to replace anonymous

classes with lambda expressions when possible.

public interface Printer {

void print();

}

Printer p = new Printer() {

@Override

public void print() {

System.out.println("Hello world!");

}

};

Printer p = () -> System.out.println("Hello world!");

Page 13: Java’s Stream APIoosd192/wiki.files/PS5.pdf · class or interface, without having to actually subclass a class. A drawback with anonymous classes is they can't have explicit constructors.

The functional interface

Basic entities you should know:

Supplier<T>

Simulates a method that takes no arguments and

returns T by calling get().

Consumer<T>

Simulates a void method that takes an argument

of type T by calling accept(T)public interface Consumer<T> {

void accept(T t);

}

public interface Supplier<T> {

T get();

}

Page 14: Java’s Stream APIoosd192/wiki.files/PS5.pdf · class or interface, without having to actually subclass a class. A drawback with anonymous classes is they can't have explicit constructors.

The functional interface

Basic entities you should know:

Function<T, K>

Simulates a method that takes an argument of

type T and returns a value of type K by calling

apply(T)

Predicate<T>

Simulates a method that takes an argument of

type T and returns a boolean indicating whether

T matches some specifications by calling test(T).

public interface Function<T, R> {

R apply(T t);

}

public interface Predicate<T> {

boolean test(T t);

}

Page 15: Java’s Stream APIoosd192/wiki.files/PS5.pdf · class or interface, without having to actually subclass a class. A drawback with anonymous classes is they can't have explicit constructors.

The Stream API

Stream is an API (Application Programming Interface) that supports functional-

style operation on streams (or collections) of elements.

Intermediate operations (such as map, filter, sorted) allows to perform

manipulation on the stream object.

Terminal operations (collect, forEach, reduce) allows to aggregate the elements

of a manipulated stream into the desired result.

Let’s take a look at some of these operations.

Assuming the following definition:

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

Page 16: Java’s Stream APIoosd192/wiki.files/PS5.pdf · class or interface, without having to actually subclass a class. A drawback with anonymous classes is they can't have explicit constructors.

Stream.map()

map(f) - Takes a lambda expression f as an argument.

f is an object of type Function<T, K>: takes T as an argument and returns K (can

be anything).

Returns a stream of elements composed of f(x) for each x in lst.

Stream<Integer> squared = lst.stream()

.map((x) -> x * x); // Values are: (1, 4, 9, 16, 25)

Page 17: Java’s Stream APIoosd192/wiki.files/PS5.pdf · class or interface, without having to actually subclass a class. A drawback with anonymous classes is they can't have explicit constructors.

Stream.filter()

filter(f) - Takes a lambda expression f as an argument.

F is an object of type Predicate<T>: takes T as an argument and returns a

boolean, indicating whether that argument matches the predicate.

Returns a stream of elements composed of each x in lst such that f(x)=true.

Stream<Integer> even = lst.stream()

.filter((x) -> x % 2 == 0); // Values are (2, 4)

Page 18: Java’s Stream APIoosd192/wiki.files/PS5.pdf · class or interface, without having to actually subclass a class. A drawback with anonymous classes is they can't have explicit constructors.

Stream.reduce()

reduce(id, f) - Takes an element id, and a lambda expression f as an argument.

id is an object of the same type T of lst.

f takes two arguments: accumulate element (starts with id) and the next element

of the list (both of the same type T), perform an operation on both of them and

return the result (also of type T), which in turn becomes the accumulated value

of the next iteration.

Returns the accumulated element once done.

int sum = lst.stream()

.reduce(0, (acc, next) -> acc + next); // Value is 15

Page 19: Java’s Stream APIoosd192/wiki.files/PS5.pdf · class or interface, without having to actually subclass a class. A drawback with anonymous classes is they can't have explicit constructors.

Stream.forEach()

forEach(f) - Takes a lambda expression f as an argument.

f is an object of type Consumer<T>: takes an element T from the stream and

perform some operations.

No value returned.

Equivalent to our famous ‘for x : lst’ loops.

We can achieve the same goal by using method-reference:

lst.stream().forEach((x) -> System.out.println(x)); // Prints the elements of lst

lst.stream().forEach(System.out::println);

Page 20: Java’s Stream APIoosd192/wiki.files/PS5.pdf · class or interface, without having to actually subclass a class. A drawback with anonymous classes is they can't have explicit constructors.

Few examples

Take a look at the following class

for practice:

And initialize the following list

within our main:

public class Character {

String name;

String title;

String city;

int level;

double hitPoints;

Continent continent;

public Character(String name, String title, String city,

int level, double hitPoints, Continent continent) {

this.name = name;

this.title = title;

this.city = city;

this.level = level;

this.hitPoints = hitPoints;

this.continent = continent;

}

}

enum Continent {

Kalimdor,

Eastern_Kingdoms,

Northrend

}

List<Character> characters = Arrays.asList(

new Character("Arthas Menethil", "Lich King", "Icecrown", 80, 1500, Continent.Northrend),

new Character("Thrall", "Warchief", "Orgrimmar", 90, 1200, Continent.Kalimdor),

new Character("Jaina Proudmoore", “Lord Admiral", "Theramore", 120, 1000, Continent.Eastern_Kingdoms),

new Character("Tyrande Whisperwind", "Priestess of Elune", "Teldrassil", 120, 1100, Continent.Kalimdor),

new Character("Sylvanas Windrunner", "Dark Ranger", "Undercity", 120, 1100, Continent.Eastern_Kingdoms)

);

Page 21: Java’s Stream APIoosd192/wiki.files/PS5.pdf · class or interface, without having to actually subclass a class. A drawback with anonymous classes is they can't have explicit constructors.

Few examples

Create a list with the names of all the characters from the continent of Kalimdor:

Note that in order to create a list out of a stream we need to call:

.collect(Collectors.toList())

List<String> names = characters.stream()

.filter((x) -> x.getContinent() == Continent.Kalimdor)

.map(Character::getName)

.collect(Collectors.toList());

Page 22: Java’s Stream APIoosd192/wiki.files/PS5.pdf · class or interface, without having to actually subclass a class. A drawback with anonymous classes is they can't have explicit constructors.

Few examples

Find the average hit points of characters at level 120:

List<Double> hitPoints = characters.stream()

.filter((character) -> character.getLevel() == 120)

.map(Character::getHitPoints)

.collect(Collectors.toList());

double average = hitPoints.stream()

.reduce(0.0, (acc, next) -> acc + next / hitPoints.size());

Page 23: Java’s Stream APIoosd192/wiki.files/PS5.pdf · class or interface, without having to actually subclass a class. A drawback with anonymous classes is they can't have explicit constructors.

Few examples

Print all characters, sorted by their hit points:

Note: we might want to implement Character::toString in order to get an

informative result.

Also note the usage of ‘Comparator.comparing’: this allows us to compare two

object of type Character without actually implementing the ‘Comparable’

interface – useful when you would like to compare elements based on different

attributes.

characters.stream()

.sorted(Comparator.comparing(Character::getHitPoints))

.forEach(System.out::println);

Page 24: Java’s Stream APIoosd192/wiki.files/PS5.pdf · class or interface, without having to actually subclass a class. A drawback with anonymous classes is they can't have explicit constructors.

Few examples

Given a list of names, initialize a list of characters with the same name, title

‘Grunt’, city ‘Orgrimmar’, level 15, continent ‘Kalimdor’ and hit points between

200 to 300:

List<String> gruntsNames = Arrays.asList("Durotan", "Grom", "Garrosh",

"Garona", "Nazgrim", "Varok");

List<Character> grunts = gruntsNames.stream()

.map((name) -> new Character(name, "Grunt", "Orgrimmar", 15,

(Math.random() * 100 + 200), Continent.Kalimdor))

.collect(Collectors.toList());