Core Java
Java 8 lambda walkthrough
For work I have made a presentation about Java 8 project lambda and of course also some simple code illustrating some of the points. The overall reasons for Java 8 are:
- More concise code (for classes that have just one method & collections). “We want the reader of the code to have to wade through as little syntax as possible before arriving at the “meat” of the lambda expression.” – Brian Goetz (http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-4.html)
- Ability to pass around functionality, not just data
- Better support for multi core processing
All examples are runnable on the following version of Java 8 downloaded from here:
openjdk version "1.8.0-ea" OpenJDK Runtime Environment (build 1.8.0-ea-lambda-nightly-h3876-20130403-b84-b00) OpenJDK 64-Bit Server VM (build 25.0-b21, mixed mode)
The simplest case:
public class ThreadA { public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { System.err.println("Hello from anonymous class"); } }).start(); } }
public class ThreadB { public static void main(String[] args) { new Thread(() -> { System.err.println("Hello from lambda"); }).start(); } }
Note the syntax, informally as
()|x|(x,..,z) -> expr|stmt
The arrow is a new operator. And note the conciseness of the second piece of code compared to the more bulky first piece.
Collections:
First let me introduce an simple domain and some helpers
public class Something { private double amount; public Something(double amount) { this.amount = amount; } public double getAmount() { return amount; } public String toString() { return "Amount: " + amount; } } public class Helper { public static List<Something> someThings() { List<Something> things = new ArrayList<>(); things.add(new Something(99.9)); things.add(new Something(199.9)); things.add(new Something(299.9)); things.add(new Something(399.9)); things.add(new Something(1199.9)); return things; } } public interface Doer<T> { void doSomething(T t); }
Lets do some filtering and sorting Java 7 style:
public class CollectionA { public static void main(String... args) { List<Something> things = Helper.someThings(); System.err.println("Filter"); List<Something> filtered = filter(things); System.err.println(filtered); System.err.println("Sum"); double sum = sum(filtered); System.err.println(sum); } public static List<Something> filter(List<Something> things) { List<Something> filtered = new ArrayList<>(); for (Something s : things) { if (s.getAmount() > 100.00) { if (s.getAmount() < 1000.00) { filtered.add(s); } } } return filtered; } public static double sum(List<Something> things) { double d = 0.0; for (Something s : things) { d += s.getAmount(); } return d; } }
And now Java 8 style – streaming:
import java.util.stream.Collectors; public class CollectionB { public static void main(String... args) { List<Something> things = Helper.someThings(); System.err.println("Filter lambda"); List<Something> filtered = things.stream().parallel().filter( t -> t.getAmount() > 100.00 && t.getAmount() < 1000.00).collect(Collectors.toList()); System.err.println(filtered); System.err.println("Sum lambda"); double sum = filtered.stream().mapToDouble(t -> t.getAmount()).sum(); System.err.println(sum); } }
The import java.util.function.* interfaces & method references
public class CollectionC { public static void main(String... args) { List<Something> things = Helper.someThings(); System.err.println("Do something"); doSomething(things, new Doer<Something>() { @Override public void doSomething(Something t) { System.err.println(t); } }); } public static void doSomething(List<Something> things, Doer<Something> doer) { for (Something s : things) { doer.doSomething(s); } } }
Replace our Doer interface with the standard Consumer interface (previously known as Block)
import java.util.function.Consumer; public class CollectionD { public static void main(String... args) { List<Something> things = Helper.someThings(); System.err.println("Do something functional interfaces"); consumeSomething(things, new Consumer<Something>() { @Override public void accept(Something t) { System.err.println(t); } }); System.err.println("Do something functional interfaces, using lambda"); consumeSomething(things, (t) -> System.err.println(t)); System.err.println("Do something functional interfaces, using lambda method reference (new operator ::) "); consumeSomething(things, System.err::println); System.err.println("Do something functional interfaces, using stream"); things.stream().forEach(new Consumer<Something>() { @Override public void accept(Something t) { System.err.println(t); } }); System.err.println("Do something functional interfaces, using stream and method reference"); things.stream().forEach(System.err::println); } public static void doSomething(List<Something> things, Doer<Something> doer) { for (Something s : things) { doer.doSomething(s); } } public static void consumeSomething(List<Something> things, Consumer<Something> consumer) { for (Something s : things) { consumer.accept(s); } } }
Map, reduce, lazy & optional
import java.util.List; import java.util.NoSuchElementException; import java.util.Optional; import java.util.stream.Collectors; public class Various { public static void main(String... args) { List<Something> things = Helper.someThings(); //Map System.err.println(things.stream().map((Something t) -> t.getAmount()).collect(Collectors.toList())); //Reduce double d = things.stream().reduce(new Something(0.0), (Something t, Something u) -> new Something(t.getAmount() + u.getAmount())).getAmount(); System.err.println(d); //Reduce again System.err.println(things.stream().reduce((Something t, Something u) -> new Something(t.getAmount() + u.getAmount())).get()); //Map/reduce System.err.println(things.stream().map((Something t) -> t.getAmount()).reduce(0.0, (x, y) -> x + y)); //Lazy Optional<Something> findFirst = things.stream().filter(t -> t.getAmount() > 1000).findFirst(); System.err.println(findFirst.get()); //Lazy no value Optional<Something> findFirstNotThere = things.stream().filter(t -> t.getAmount() > 2000).findFirst(); try { System.err.println(findFirstNotThere.get()); } catch (NoSuchElementException e) { System.err.println("Optional was not null, but its value was"); } //Optional one step deeper things.stream().filter(t -> t.getAmount() > 1000).findFirst().ifPresent(t -> System.err.println("Here I am")); } }
Reference: Java 8 lambda walkthrough from our JCG partner Kim Saabye Pedersen at the Kim Saabye Pedersen’s blog blog.