Copyright 2017 - Jacob D. Parr
https://www.jacobparr.com
(all rights reserved)
Functional Java
Functional Java
Functional Java
Functional Java
Functional Java
Functional Java
Functional Java
Functional Java
Functional Java
Functional Java
Functional Java
Functional Java
Functional Java
public class Chapter2 extends Chapter2Abstract {
public boolean isBalanceSufficient(Account account, double amount) {
logAccess(account);
boolean isBalanceSufficient = account.getBalance() - amount > 0;
if (isBalanceSufficient == false) {
// It would be nice to let the caller vary this condition
if (account.getCreditRating() > 700) {
isBalanceSufficient = true;
alertOverdraw(account);
}
}
return isBalanceSufficient;
}
}
Functional Java
if (account.getCreditRating() > 700) {
Functional Java
// Part #1
public boolean isBalanceSufficient(Account account, double amount) {
logAccess(account);
return account.getBalance() - amount > 0;
}
// Part #2
public void doOverdraft(Account account, double amount) {
alertOverdraw(account);
}
// Part #3
public void doSomething(Account anAccount, double anAmount) {
if ( isBalanceSufficient(anAccount, anAmount) == false) {
// Caller can now vary the condition and control the flow
if (anAccount.getCreditRating() > 700) {
doOverdraft(anAccount, anAmount);
}
}
}
Functional Java
public interface Exemptable {
boolean isExempt(Account account);
}
Functional Java
public boolean isBalanceSufficient(Account account, double amount, Exemptable ex) {
logAccess(account);
boolean isBalanceSufficient = account.getBalance() - amount > 0;
if (isBalanceSufficient == false) {
if (ex.isExempt(account)) {
isBalanceSufficient = true;
alertOverdraw(account);
}
}
return isBalanceSufficient;
}
Functional Java
public void doSomething(Account anAccount, double anAmount) {
isBalanceSufficient(anAccount, anAmount, new Exemptable() {
@Override
public boolean isExempt(Account account) {
return account.getCreditRating() > 700;
}
});
}
Functional Java
Functional Java
What we want is to pass the behavior.
Functional Java
1 2 3 (Parameter declaration) -> {Lambda body}
Functional Java
public void doSomething_1(Account anAccount, double anAmount) {
Exemptable ex = (Account acct) -> {return acct.getCreditRating() > 700; };
isBalanceSufficient(anAccount, anAmount, ex);
}
Functional Java
Here's the logic:
Functional Java
Functional Java
public void doSomething_2(Account anAccount, double anAmount) {
Exemptable ex = (Account acct) -> acct.getCreditRating() > 700;
isBalanceSufficient(anAccount, anAmount, ex);
}
Functional Java
public void doSomething_3(Account anAccount, double anAmount) {
Exemptable ex = (acct) -> acct.getCreditRating() > 700;
isBalanceSufficient(anAccount, anAmount, ex);
}
Functional Java
public void doSomething_4(Account anAccount, double anAmount) {
Exemptable ex = acct -> acct.getCreditRating() > 700;
isBalanceSufficient(anAccount, anAmount, ex);
}
Functional Java
public void doSomething_5(Account anAccount, double anAmount) {
isBalanceSufficient(anAccount, anAmount, acct -> acct.getCreditRating() > 700);
}
Functional Java
Functional Java
@FunctionalInterface
public interface Exemptable {
boolean isExempt(Account account);
// won't compile.
boolean someOtherFunction();
}
Functional Java
@FunctionalInterface
interface AccountExemptionHandler {
public void onAccountExempted(Account account);
}
Functional Java
public boolean isBalanceSufficient(Account account, double amount, Exemptable ex, AccountExemptionHandler handler) {
logAccess(account);
boolean isBalanceSufficient = account.getBalance() - amount > 0;
if (isBalanceSufficient == false) {
if (ex.isExempt(account)) {
isBalanceSufficient = true;
// Give the caller the opportunity to do something extra
handler.onAccountExempted(account);
}
}
return isBalanceSufficient;
}
Functional Java
public void doSomething_1(Account anAccount, double anAmount) {
AccountExemptionHandler anonymousAccountExemptionHandler = new AccountExemptionHandler() {
@Override
public void onAccountExempted(Account account) {
System.out.println(account);
}
};
isBalanceSufficient(
anAccount,
anAmount,
acct -> acct.getCreditRating() > 700,
anonymousAccountExemptionHandler);
}
Functional Java
public void doSomething_2(Account anAccount, double anAmount) {
AccountExemptionHandler anonymousAccountExemptionHandler = new AccountExemptionHandler() {
@Override
public void onAccountExempted(Account account) {
System.out.println(account);
}
};
isBalanceSufficient(
anAccount,
anAmount,
acct -> acct.getCreditRating() > 700,
acct -> System.out.println(acct) );
}
Functional Java
public void doSomething_3(Account anAccount, double anAmount) {
AccountExemptionHandler anonymousAccountExemptionHandler = new AccountExemptionHandler() {
@Override
public void onAccountExempted(Account account) {
System.out.println(account);
}
};
isBalanceSufficient(
anAccount,
anAmount,
acct -> acct.getCreditRating() > 700,
System.out::println);
}
Functional Java
This works because...
void println(Object)
matches
void onAccountExempted(Account
Functional Java
Functional Java
Functional Java
isBalanceSufficient(
anAccount,
anAmount,
acct -> acct.getCreditRating() > 700, // cannot use a method reference here
System.out::println);
public static boolean defaultExemption(Account account) {
return account.getCreditRating() > 700;
}
isBalanceSufficient(
anAccount,
anAmount,
Banker::defaultExemption, // THIS WORKS
System.out::println);
Functional Java
Two key points
Additionally, this gives us choices: Lambda expression, blocks or method references
Functional Java
6 Different Varieties
Functional Java
public void runTheBank_1(Account account, double amount) {
new Banker() {
public void runTheBank(Account account, double amount) {
isBalanceSufficient(account, amount,
Banker::defaultExemption, // Static method reference
this::instanceAccountExemptionHandler // Instance method reference
);
}
};
}
public void runTheBank_2(Account account, double amount) {
Banker banker = new Banker();
banker.isBalanceSufficient(account, amount,
Banker::defaultExemption, // Static method reference
banker::instanceAccountExemptionHandler // Instance method reference
);
}
Functional Java
public void runTheBank_3(Account account, double amount) {
new Banker() {
public void runTheBank(Account account, double amount) {
isBalanceSufficient(account, amount,
Banker::defaultExemption, // Static method reference
super::superAccountExemptionHandler // Super method reference
);
}
};
}
Functional Java
@FunctionalInterface
public interface AccountCreator {
public Account create(String aDefault);
}
@FunctionalInterface
public interface ListCreator<T> {
public T create();
}
@FunctionalInterface
public interface AccountArrayCreator {
public Account[] create(int count);
}
Functional Java
Take a look at makeDefaultAccounts(..)
public List<Account> makeDefaultAccounts (int count,
AccountCreator accountCreator,
ListCreator<List<Account>> listCreator) {
List<Account> returnList = listCreator.create();
for (int index = 0; index < count; ++index) {
returnList.add(accountCreator.create("default"));
}
return returnList;
}
Functional Java
public void runTheBank_4() {
Banker banker = new Banker();
banker.makeDefaultAccounts(10,
id -> new Account(id),
() -> new LinkedList<>());
}
public void runTheBank_5() {
Banker banker = new Banker();
banker.makeDefaultAccounts(10,
Account::new, // Constructor Reference
LinkedList<Account>::new); // Generic Constructor Reference
}
Functional Java
Take a look at makeArrayDefaultAccounts(..)
public Account[] makeArrayDefaultAccounts (int count,
AccountCreator accountCreator,
AccountArrayCreator accountArrayCreator) {
Account[] returnList = accountArrayCreator.create(count);
for (int index=0; index < count; ++index) {
returnList[index] = accountCreator.create("default");
}
return returnList;
}
Functional Java
public void runTheBank_6() {
Banker banker = new Banker();
banker.makeArrayDefaultAccounts(10,
Account::new,
size -> new Account[size]);
}
public void runTheBank_7() {
Banker banker = new Banker();
banker.makeArrayDefaultAccounts(10,
Account::new,
Account[]::new);
}
Functional Java
We've created our own functional interfaces:
This was purely acedemic
Functional Java
Functional Java
public abstract class ObjectOrientedTimedRunnable implements Runnable {
private long executionDuration;
@Override
public abstract void run();
public final void runTimed() throws InterruptedException {
long startTime = System.currentTimeMillis();
Thread thread = new Thread(this);
thread.start();
thread.join();
executionDuration = System.currentTimeMillis() - startTime;
}
public long getExecutionDuration() {
return executionDuration;
}
}
Functional Java
Designed in an object-oriented conversational style forcing us to use anonymous classes because...
We can fix this with a rewrite, but we have to address all three issues listed above.
Functional Java
public abstract class StatelessTimedRunnable implements Runnable {
public abstract void run();
public long runTimed() throws InterruptedException {
System.out.println("Running StatelessTimedRunnable");
long startTime = System.currentTimeMillis();
Thread thread = new Thread(this);
thread.start();
thread.join();
return System.currentTimeMillis() - startTime;
}
}
Functional Java
default void doSomething(Object takeSomething) {
// do something
}
Functional Java
@FunctionalInterface
public interface FunctionalTimedRunnable extends Runnable {
default long runTimed() throws InterruptedException {
printInterfaceName("FunctionalTimedRunnable");
long startTime = System.currentTimeMillis();
Thread thread = new Thread(this);
thread.start();
thread.join();
return System.currentTimeMillis() - startTime;
}
default void launchTest() throws InterruptedException {
System.out.println("This thread executed for: " + runTimed() + " ms.");
}
default void printInterfaceName(String interfaceName) {
System.out.println("Running " + interfaceName);
}
}
Functional Java
public void timeThis_2() throws InterruptedException {
FunctionalTimedRunnable timedRunnable = () -> { /* do something */};
timedRunnable.launchTest();
}
Functional Java
Functional Java
@FunctionalInterface
public interface AverageTimedRunnable extends FunctionalTimedRunnable {
default long runTimed() throws InterruptedException {
printInterfaceName("AverageTimedRunnable");
long timeSum = 0;
for (int count = 0; count < 5; ++count) {
timeSum += FunctionalTimedRunnable.super.runTimed();
}
return Math.round(timeSum / 5);
}
}
Functional Java
Functional Java
@FunctionalInterface
public interface MedianTimedRunnable extends FunctionalTimedRunnable {
default long runTimed() throws InterruptedException {
printInterfaceName("MedianTimedRunnable ");
final int sampleSize = 5;
ArrayList<Long> longs = new ArrayList<>(sampleSize);
for (int count = 0; count < sampleSize; ++count) {
longs.add(FunctionalTimedRunnable.super.runTimed());
}
Collections.sort(longs);
return longs.get(sampleSize / 2);
}
}
Functional Java
@FunctionalInterface
public interface SmartTimedRunnable extends AverageTimedRunnable, MedianTimedRunnable {
default long runTimed() throws InterruptedException {
printInterfaceName("SmartTimedRunnable");
long averageElapsedTime = AverageTimedRunnable.super.runTimed();
// Choose average time if less than 1 second
if (averageElapsedTime < 1000) {
return averageElapsedTime;
} else {
return MedianTimedRunnable.super.runTimed();
}
}
}
Functional Java
Functional Java
@FunctionalInterface
public interface SmartTimedRunnable extends AverageTimedRunnable, MedianTimedRunnable {
default long runTimed() throws InterruptedException {
printInterfaceName("SmartTimedRunnable");
long averageElapsedTime = AverageTimedRunnable.super.runTimed();
// Choose average time if less than 1 second
if (averageElapsedTime < 1000) {
return averageElapsedTime;
} else {
return MedianTimedRunnable.super.runTimed();
}
}
}
Functional Java
When not specifically declared, Java has 3 rules for resolving conflicts caused by multiple inheritence
Said another way...
Functional Java
Functional Java
Functional Java
@FunctionalInterface
public interface StaticTimedRunnable extends Runnable {
static long runTimed(StaticTimedRunnable timedRunnable) throws InterruptedException {
printInterfaceName("StaticTimedRunnable");
long startTime = System.currentTimeMillis();
Thread thread = new Thread(timedRunnable);
thread.start();
thread.join();
return System.currentTimeMillis() - startTime;
}
static void launchTest(StaticTimedRunnable runnable) throws InterruptedException {
System.out.println("This thread executed for: " + runTimed(runnable) + " ms.");
}
static void printInterfaceName(String interfaceName) {
System.out.println("Running " + interfaceName);
}
}
Functional Java
public void timeThis_3() throws InterruptedException {
StaticTimedRunnable.launchTest( () -> {/* do something */} );
}
Functional Java
Functional Java
Functional Java
Let's start with a new functional interface...
@FunctionalInterface
public interface LambdaExecutor {
public void execute(Object object);
}
Functional Java
public class LexicalScoping {
private Object attribute;
public void readWriteClassAttributes() throws Exception {
// attribute is a class attribute that can be read and
// written inside of lambdas
LambdaExecutor executor = (anInteger) -> attribute = anInteger;
executor.execute(1);
System.out.println(attribute); // prints 1
}
}
Functional Java
public class LexicalScoping {
public void readLocalVariable() {
// No need to declare localAttribute as final in Java 8
Object localAttribute = 1;
// Accessing a local attribute within a lambda is permitted
LambdaExecutor executor = anObject ->
System.out.println("Accessing localAttribute: " + localAttribute);
executor.execute(null);
}
}
Functional Java
public void writeEffectivelyFinal() {
Object localAttribute = 1;
// localAttribute will now lose its effectively final status and
// will no longer be allowed within the lambda.
localAttribute = 2;
LambdaExecutor executor = anInteger ->
System.out.println("Accessing localAttribute: " + localAttribute);
executor.execute(null); }
}
Functional Java
public LambdaExecutor getLambdaExecutor() {
Object localAttribute = 1;
// Not legal because localAttribute annot be modified
return anObject -> localAttribute = 2;
}
Functional Java
public void shadowLocalVariables() {
Object localAttribute; // 1st declaration
LambdaExecutor executor = localAttribute -> // 2nd declaration
System.out.println("Accessing localAttribute: " + localAttribute);
executor.execute(1);
}
public void shadowLocalVariablesAnonymous() {
Object localAttribute;
LambdaExecutor executor = new LambdaExecutor() {
@Override public void execute(Object localAttribute) {
// localAttribute shadowing is legal for anonymous classes
// Do something
}
};
executor.execute(1);
}
Functional Java
public void mutatingState() {
// Assign a StringBuffer to the class attribute named attribute
attribute = new StringBuffer();
LambdaExecutor executor = aString ->
// Inner state of attribute is mutated inside the lambda
((StringBuffer) attribute).append(aString); executor.execute("another string");
}
Functional Java
Functional Java
Functional Java
new StringBuilder("Hello").append("There");
@FunctionalInterface
public interface StringExecutor {
public String execute(String text);
}
public void test_2() {
(StringExecutor executor = s -> s + "There").execute("Hello");
}
Functional Java
Functional Java
Functional Java
Functional Java
Did you say 40?
Functional Java
Functional Java
Archetype
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
Abstract
Consume and discard
Variants
Functional Java
Functional Java
public static double computeShapeArea(int angles, double measure1, double measure2, Consumer<String> consumer) {
double area;
switch (angles) {
case 0: {
area = Math.PI * Math.pow(measure1, 2);
consumer.accept("Area formula for shape with " + angles + " angles is pi times radius: " + Math.PI + " times " + measure1 + " squared = " + area);
break;
}
case 3: {
area = measure1 * measure2 / 2;
consumer.accept("Area formula for shape with " + angles + " angles is height times base divided by“ + “ 2: " + measure1 + " times " + measure2 + " divided by 2 = " + area);
break;
}
case 4: {
area = measure1 * measure2;
consumer.accept("Area formula for shape with " + angles + " angles is width times length: " + measure1 + " times" + measure2 + " = " + area);
break;
}
default: {
throw new RuntimeException("Unsupported shape with" + angles + "angles");
}
}
return area;
}
Functional Java
public void family_1() {
computeShapeArea(0, 7, 0, System.out::println); // Circle
computeShapeArea(3, 8, 2, System.out::println); // Triangle
computeShapeArea(4, 10, 5, System.out::println); // Rectangle
}
Area formula for shape with 0 angles is pi times radius:
3.141592653589793 times 7.0 squared = 153.93804002589985
Area formula for shape with 3 angles is height times base divided
by 2: 8.0 times 2.0 divided by 2 = 8.0
Area formula for shape with 4 angles is width times length: 10.0
times 5.0 = 50.0
Functional Java
Archetype
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
Abstract
Map, transform, compute
Variants
Functional Java
public static double computeShapeArea(double measure1,
double measure2,
BiFunction<Double,Double,Double> function) {
return function.apply(measure1, measure2);
}
public void family_2() {
computeShapeArea(7, 0, (m1,m2) -> Math.PI * Math.pow(m1, 2)); // Circle
computeShapeArea(8, 2, (m1,m2) -> m1 * m1 / 2); // Triangle
computeShapeArea(10, 5, (m1,m2) -> m1 * m2); // Rectangle
}
Functional Java
Archetype
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
Abstract
Test, Filter
Variants
Functional Java
public static double computeShapeArea(double measure1,
double measure2,
DoubleBinaryOperator function,
BiPredicate<Double, Double> roundUpPredicate) {
double area = function.applyAsDouble(measure1, measure2);
// Let the predicate decide to round or not
return roundUpPredicate.test(measure1, measure2) ? Math.rint(area) : area;
}
public void test() {
computeShapeArea(7, 0, (r, whatever) -> Math.PI * Math.pow(r, 2), // Circle
(r, whatever) -> r > 100);
computeShapeArea(8, 2, (b, h) -> b * h / 2, // Triangle
(b, h) -> b > 100 || h > 100);
computeShapeArea(10, 5, (w, h) -> w * h, // Rectangle
(w, h) -> w > 100 || h > 100);
}
Functional Java
Archetype
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
Abstract
Create
Variants
Functional Java
public static double computeShapeArea(Supplier<Double> supplier1, Supplier<Double> supplier2,
DoubleBinaryOperator function) {
return function.applyAsDouble(supplier1.get(), supplier2.get());
}
public void test() {
computeShapeArea(
() -> 7.0,
() -> 0.0,
(r, whatever) -> Math.PI * Math.pow(r, 2)); // Circle
computeShapeArea(
() -> 8.0,
() -> 2.0,
(b, h) -> b * h / 2); // Triangle
computeShapeArea(
() -> 10.0,
() -> 5.0,
(w, h) -> w * h); // Rectangle
}
Functional Java
Functional Java
Functional Java
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
Functional Java
Logger logger = Logger.getLogger("ConsumerLogger");
Consumer<String> printToOut = System.out::println;
Consumer<String> printToLog = (s) -> {
// Log the message as severe if it contains the work "critical"
if (s.contains("critical")) {
logger.severe(s);
} else {
logger.info(s);
}
};
public void test() {
Consumer<String> superPrinter = printToOut.andThen(printToLog);
// or
superPrinter = printToOut.andThen(printToLog).andThen(s -> {/* and yet one more thing */} );
}
Functional Java
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
default Predicate<T> negate() {
return (t) -> !test(t);
}
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
Functional Java
IntPredicate isIndivisible = i -> {
for (int n = 2; n <= Math.sqrt(i); ++n) {
if (i % n == 0) return false;
}
return true;
};
IntPredicate isGreaterThanOne = i -> i > 1;
IntPredicate isPrime = isGreaterThanOne.and(isIndivisible);
IntPredicate isComposite = isIndivisible.negate().and(isGreaterThanOne);
Functional Java
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
Functional Java
Function<List<String>, List<String>> ps = null; // whatever
Function<List<String>, List<String>> grep = null; // whatever
Function<List<String>, List<String>> listJavaProcs = ps.andThen(grep);
public void test1() {
for (String nextProcess : listJavaProcs.apply(null)) {
System.out.println(nextProcess);
}
}
Function<List<String>, List<String>> wcLInes = null; // whatever
public void test2() {
ps.andThen(grep).andThen(wcLInes);
}
Functional Java
Function<List<String>, List<String>> which = null; // whatever
Function<List<String>, List<String>> vi = null; // whatever
Function<List<String>, List<String>> viWhich = vi.compose(which);
Function<List<String>, List<String>> whichVi = which.andThen(vi);
Functional Java
Functional Java
@Override
public void forEach(Consumer<? super E> action) {
c.forEach(action);
}
public void test1() {
Collection<String> stooges = Arrays.asList("Larry", "Moe", "Curly");
// Print the contents of the stooges collection
stooges.forEach(System.out::println);
}
Functional Java
@Override
public boolean removeIf(Predicate<? super E> filter) {
throw new UnsupportedOperationException();
}
public void test2() {
Collection<String> stooges = Arrays.asList("Larry", "Moe", "Curly", "Tom", "Dick", "Harry");
Predicate<String> isAStooge = s -> "Larry".equals(s) || "Moe".equals(s) || "Curly".equals(s);
stooges.removeIf(isAStooge.negate());
}
Functional Java
@Override
public void replaceAll(UnaryOperator<E> operator) {
throw new UnsupportedOperationException();
}
public void test3() {
List<String> stooges = new ArrayList<>(Arrays.asList("Larry", "Moe", "Curly"));
UnaryOperator<String> feminize = s -> {
if ("Larry".equals(s)) return "Lara";
if ("Moe".equals(s)) return "Maude";
if ("Curly".equals(s)) return "Shirley";
return s;
};
stooges.replaceAll(feminize);
}
Functional Java
private Map<Integer, List<String>> movieDatabase = new HashMap<>();
public void addMovieOO(Integer year, String title) {
List<String> movies = movieDatabase.get(year);
if (movies == null) {
// Need to create the array list if it doesn't yet exist
movies = new LinkedList<String>();
movieDatabase.put(year, movies);
}
movies.add(title);
}
Functional Java
V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
default V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)
default V getOrDefault(Object key, V defaultValue)
default V putIfAbsent(K key, V value)
default V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction)
Functional Java
private Map<Integer, List<String>> movieDatabase = new HashMap<>();
public void addMovieFP(Integer year, String title) {
movieDatabase.computeIfAbsent( year, k -> new LinkedList<>());
movieDatabase.compute(year, (key, value) -> {
value.add(title);
return value;
});
}
movieDatabase.putIfAbsent(year, new LinkedList<>());
movieDatabase.compute(year, (key, value) -> {
value.add(title);
return value;
});
Functional Java
public void getMovie(Integer year) {
movieDatabase.getOrDefault(year, new LinkedList<>());
}
public void addMovies(Integer year, List<String> titles) {
movieDatabase.merge(year, titles, (t1,t2) -> {
t1.addAll(t2);
return t1;
});
}
Functional Java
public void addMovieFP3(Integer year, String title) {
movieDatabase.computeIfAbsent(year, k -> new LinkedList<>())
.add(title);
}
Functional Java
Spliterator<T> trySplit();
void forEachRemaining(T_CONS action)
boolean tryAdvance(IntConsumer action)
Functional Java
Functional Java
public boolean isMovieInLIst(String title, List<String> movieList) throws InterruptedException {
// Obtain a spliterator from the movie list
Spliterator<String> s1 = movieList.spliterator();
// Now split the original list in half
Spliterator<String> s2 = s1.trySplit();
AtomicBoolean isFound = new AtomicBoolean(false);
if (s2 == null) return isFound.get();
Consumer<String> finder = movie -> {
if (movie.equals(title)) isFound.set(true);
};
Thread t1 = new Thread( () -> s1.forEachRemaining(finder) );
Thread t2 = new Thread( () -> s2.forEachRemaining(finder) );
t1.start();
t2.start();
t1.join();
t2.join();
return isFound.get();
}
Functional Java
Functional Java
Functional Java
Functional Java
Functional Java
What if we need to scale?
Functional Java
Functional Java
Functional Java
What if we distribute these workers in a different way?
Functional Java
Functional Java
Let's break it down:
Functional Java
Functional Java
Functional Java
concat(Stream<? extends T> a, Stream<? extends T> b) Inter Stat empty() Inter Stat generate(Supplier<T> s) Inter Stat iterate(final T seed, final UnaryOperator<T> f) Inter Stat of(T t) Inter Stat of(T... values) Inter Stat onClose(Runnable closeHandler) Inter Inst parallel() Inter Inst sequential() Inter Inst skip(final T seed, final UnaryOperator<T> f) Inter Inst sorted() Inter Inst sorted(Comparator<? super T> comparator) Inter Inst unordered() Inter Inst builder() Term Stat close() Term Inst iterator(final T seed, final UnaryOperator<T> f) Term Inst spliterator() Term Inst toArray() Term Inst toArray(IntFunction<A[]> generator) Term Inst
Abstract: Create and start the stream process
Functional Java
Functional Java
public void test1() {
Stream.of(0, 1, 2, 3, 4, 5, 6, 7, 8 ,9);
}
public void test2() {
Integer[] integers = Stream
.of(0, 1, 2, 3, 4, 5, 6, 7, 8 ,9)
.toArray(Integer[]::new);
}
Functional Java
public void test() {
Stream.iterate(1, i -> ++i);
}
Functional Java
public void test4() {
Integer[] integers = Stream.of(1, 2, 3).toArray(Integer[]::new);
}
public void test5() {
Stream.Builder<Integer> builder = Stream.builder();
for (int i = 0; i < 100; i++) {
builder.add(i);
}
builder.build(); // Building is done
builder.add(9999); // throws IllegalStateException
}
Functional Java
Functional Java
forEach(Consumer<? super T> action) Term Inst forEachOrdered(Consumer<? super T> action) Term Inst forEach(Consumer<? super T> action) Term Inst
public void test6() {
Stream.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
.forEach(System.out::println);
}
Abstract: Iterate through a stream
Functional Java
distinct() Inter Inst filter(Predicate<? super T> predicate) Inter Inst limit(long maxSize) Inter Inst
public void test7() {
Stream.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
.filter(i -> i % 2 == 0)
.forEach(System.out::println);
}
Abstract: Filter out elements from the stream
Functional Java
public void test8() {
Stream.iterate(1, i -> ++i)
.limit(10)
.filter(i -> i % 2 == 0)
.forEach(System.out::println);
}
public void test9() {
Stream.of(1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 5)
.distinct()
.forEach(System.out::println);
}
Functional Java
flatMap(Function<? super T, ? extends Stream<? extends R>> mapper) Inter Inst flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper) Inter Inst flatMapToInt(Function<? super T, ? extends IntStream> mapper) Inter Inst flatMapToLong( Function<? super T, ? extends LongStream> mapper) Inter Inst map(Function<? super T, ? extends R> mapper) Inter Inst mapToDouble(ToDoubleFunction<? super T> mapper) Inter Inst mapToInt(ToIntFunction<? super T> mapper) Inter Inst mapToLong(ToLongFunction<? super T> mapper) Inter Inst
Abstract: Maps one value to another / same or different kind
Functional Java
public void test10() {
Stream.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
.map(i -> i * 2)
.forEach(System.out::println);
}
public void test11() {
Stream.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
.map(i -> i % 2 == 0 ? i+": even" : i+": odd")
.forEach(System.out::println);
}
Functional Java
public void test12() {
Stream.of("I am a chicken", "We like to sing", "Who was that")
.flatMap( s -> Arrays.stream(s.split(" ")) )
.forEach(System.out::println);
}
I am a chicken We like to sing Who was that
Output:
Functional Java
allMatch(Predicate<? super T> predicate) Term Inst anyMatch(Predicate<? super T> predicate) Term Inst collect(Collector<? super T, A, R> collector) Term Inst collect(Supplier<R> supplier, Term Inst BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner) count() Term Inst findAny() Term Inst findFirst() Term Inst max(Comparator<? super T> comparator) Term Inst min(Comparator<? super T> comparator) Term Inst noneMatch(Predicate<? super T> predicate) Term Inst reduce(BinaryOperator<T> accumulator) Term Inst reduce(T identity, Term Inst BinaryOperator<T> accumulator) reduce(U identity, Term Inst BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner)
Abstract: Reduce a stream to a single entity
Functional Java
Functional Java
public void test13() {
int sum = Stream
.of(1, 2, 3, 4, 5)
.reduce(0, (l, r) -> l + r);
}
Functional Java
(((((0 + 1) + 2) + 3) + 4) +5) ((((1 + 2) + 3) + 4) +5) (((3 + 3) + 4) +5) ((6 + 4) +5) (10 + 5)
15
{l = 0; r = 1} {l = 1; r = 2} {l = 3; r = 3} {l = 6; r = 4} {l = 10; r = 5}
Functional Java
public void test15() {
int sum = Stream
.of(1, 3, 5)
.filter(i -> i % 2 == 0)
.reduce((l, r) -> l + r)
.orElse(0);
}
Functional Java
T get() boolean isPresent() void ifPresent(Consumer<? super T> consumer) T orElse(T other) T orElseGet(Supplier<? extends T> other) T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X Optional<U> map(Function<? super T, ? extends U> mapper) Optional<U> flatMap(Function<? super T, Optional<U>> mapper) Optional<T> filter(Predicate<? super T> predicate) Optional<T> empty() Optional<T> of(T value) Optional<U> ofNullable(T value)
Abstract: Provides contingencies when ret. values from meths.
Functional Java
public void test16() {
int sum = Stream
.of(-20, 2, 4, 6)
.reduce( (l,r) -> l + r )
.filter(i -> i > 0)
.orElse(0);
}
Functional Java
boolean found = Stream.of("Villeray", "Plateau", "Rosemont", "Mile-End")
.anyMatch("Rosemont"::equals);
boolean found = Stream.of("Villeray", "Plateau", "Rosemont", "Mile-End")
.filter("Rosemont"::equals)
.findFirst()
.isPresent();
Functional Java
Map<String,Integer> population = new HashMap<>();
population.put("Villeray", 145000);
population.put("Plateau", 100390);
population.put("Rosemont", 134038);
population.put("Mile-End", 31910);
String densePlaces = population
.keySet()
.stream()
.filter(s -> population.getOrDefault(s, 0) > 110_000)
.reduce("", String::concat);
Functional Java
Functional Java
Map<String,Integer> population = new HashMap<>();
population.put("Villeray", 145000);
population.put("Plateau", 100390);
population.put("Rosemont", 134038);
population.put("Mile-End", 31910);
List<String> densePlaces = population
.keySet()
.stream()
.filter(s -> population.getOrDefault(s, 0) > 110_000)
.collect(ArrayList::new, // Supplier
ArrayList::add, // Accumulator
ArrayList::addAll); // Combiner
Functional Java
Functional Java
peek(Consumer<? super T) action) Inter Inst
Abstract: Inject orthogonal, non-interfering behavior
Stream.iterate(1, i-> ++i)
.limit(10)
.peek(i -> System.out.println("Before: " + i))
.map(i -> i * 3)
.peek(i -> System.out.println("After: " + i))
.forEach(System.out::println);
Functional Java
Streams Must Be Coherent
Functional Java
Stream.of(1, 2, 3, 4, 5).peek(System.out::println);
Stream.of(1, 2, 3, 4, 5).peek(System.out::println).findFirst();
Stream.generate( () -> 1).allMatch(i -> i == 1);
Stream.generate( () -> 1).limit(100).allMatch(i -> i == 1);
Functional Java
List<String> words = new ArrayList<>(
Arrays.asList("This", "Sentence", "contains", "five", "words"));
words.stream()
.forEach(s -> {
if (s.equals("five")) words.add("thousands");
});
Functional Java
Stream.iterate(1, i -> ++i).limit(10).count();
IntStream.range(1, 10).count();
Functional Java
public static void printTestStats(int[] classOneScores, int[] classTwoScores) {
IntSummaryStatistics classOneStats = IntStream.of(classOneScores).summaryStatistics();
System.out.format("%s, %s, %s, %s",
classOneStats.getMax(),
classOneStats.getMin(),
classOneStats.getAverage(),
classOneStats.getCount());
IntSummaryStatistics classTwoStats = IntStream.of(classTwoScores).summaryStatistics();
System.out.format("%s, %s, %s, %s",
classTwoStats.getMax(),
classTwoStats.getMin(),
classTwoStats.getAverage(),
classTwoStats.getCount());
// Now combine the two
IntSummaryStatistics combinedStats = new IntSummaryStatistics();
combinedStats.combine(classOneStats);
combinedStats.combine(classTwoStats);
System.out.format("%s, %s, %s, %s",
combinedStats.getMax(),
combinedStats.getMin(),
combinedStats.getAverage(),
combinedStats.getCount());
}
Functional Java
Functional Java
Functional Java
public static boolean isPrimeImperative(int n) {
if (n <= 1) return false;
if (n == 2) return true;
int limit = (int)Math.sqrt(n);
for (int i = 2; i <= limit; ++i) {
if (n % i == 0) return false;
}
return true;
}
Functional Java
public static boolean isPrimeStream(int n) {
if (n <= 1) return false;
if (n == 2) return true;
return IntStream .rangeClosed(2, (int)Math.sqrt(n))
.noneMatch(i -> n % i == 0);
}
IntStream.rangeClosed(1, 100)
.filter(PrimeFinder::isPrimeStream)
.forEach(System.out::println);
Functional Java
public static boolean isPerfectImperative(long n) {
long sum = 0;
for (long i = 1; i <= n /2; i++) {
if (n % i == 0) {
sum += 1;
}
}
return sum == n;
}
public static boolean isPerfectStream(long n) {
long sum = LongStream.rangeClosed(1, n/2)
.filter(i -> n % i == 0)
.reduce(0, (l,r) -> l + r);
return (sum == n) && (n > 0);
}
As a side note...
public static List<Long> findPerfectNumbers(long maxLong, LongPredicate condition) {
return LongStream
.rangeClosed(1, maxLong)
.filter(condition)
.collect(ArrayList<Long>::new,
ArrayList<Long>::add,
ArrayList<Long>::addAll);
}
PerfectNumberFinder
.findPerfectNumbers(8128, PerfectNumberFinder::isPerfectStream)
.forEach(System.out::println);
Functional Java
Functional Java
Functional Java
public static boolean isPerfectParallel(long n) {
long sum = LongStream.rangeClosed(1, n/2)
.parallel()
.filter(i -> n % i == 0)
.reduce(0, (l,r) -> l + r);
return (sum == n) && (n > 0);
}
public static List<Long> findPerfectNumbers(long maxLong, LongPredicate condition) {
return LongStream
.rangeClosed(1, maxLong)
.parallel()
.filter(condition)
.collect(ArrayList<Long>::new,
ArrayList<Long>::add,
ArrayList<Long>::addAll);
}
Functional Java
PerfectNumberFinder
.findPerfectNumbers(8128, PerfectNumberFinder::isPerfectParallel)
.forEach(System.out::println);
Functional Java
Number to Test | isPerfectImperative | IsPerfectStream | isPerfectParallelized |
---|---|---|---|
8,128 | 0 | 1 | 0 |
33,550,336 | 190 | 229 | 66 |
8,589,869,056 | 48,648 | 59,646 | 13,383 |
137,438,691,328 | 778,853 | 998,776 | 203,651 |
Functional Java
Functional Java
Some of the pros include
Functional Java
Some of the cons include
Functional Java
Functional Java
Parallel streams work best when...
Functional Java
Parallel streams work best when...
Functional Java
Functional Java
Functional Java
Functional Java
Functional Java
Functional Java
Functional Java
Functional Java
Functional Java
Functional Java
Functional Java