diff --git a/README.md b/README.md index 0effffd8..9ee848d5 100644 --- a/README.md +++ b/README.md @@ -3,35 +3,40 @@ Java8InAction This repository contains all the source code for the examples and quizzes in the book Java 8 in Action: Lambdas, Streams and functional-style programming. -You can purchase the early access here: [http://manning.com/urma/](http://manning.com/urma/) - -We are very keen to hear your feedback and improve the book based on your comments! +You can purchase the book here: [http://manning.com/urma/](http://manning.com/urma/) or on Amazon The source code for all examples can be found in the directory [src/main/java/lambdasinaction](https://github.com/java8/Java8InAction/tree/master/src/main/java/lambdasinaction) -* Chapter 1: Introduction -* Chapter 2: Passing Code/Behaviour Parameterisation +* Chapter 1: Java 8: why should you care? +* Chapter 2: Passing code with behavior parameterization * Chapter 3: Lambda expressions -* Chapter 4: Streams -* Chapter 5: Collectors -* Chapter 6: Parallel streams and performance -* Chapter 7: Tools, testing, debugging -* Chapter 8: Default methods -* Chapter 9: Optional: a better alternative to null -* Chapter 10: CompletableFuture: composable asynchronous programming - +* Chapter 4: Working with Streams +* Chapter 5: Processing data with streams +* Chapter 6: Collecting data with streams +* Chapter 7: Parallel data processing and performance +* Chapter 8: Refactoring, testing, debugging +* Chapter 9: Default methods +* Chapter 10: Using Optional as a better alternative to null +* Chapter 11: CompletableFuture: composable asynchronous programming +* Chapter 12: New Date and Time API +* Chapter 13: Thinking functionally +* Chapter 14: Functional programming techniques +* Chapter 15: Blending OOP and FP: comparing Java 8 and Scala +* Chapter 16: Conclusions and "where next" for Java +* Appendix A: Miscellaneous language updates +* Appendix B: Miscellaneous library updates +* Appendix C: Performing multiple operations in parallel on a Stream +* Appendix D: Lambdas and JVM bytecode We will update the repository as we update the book. Stay tuned! ### Make sure to have JDK8 installed -For example: +The latest binary can be found here: http://www.oracle.com/technetwork/java/javase/overview/java8-2100321.html $ java -version -java version "1.8.0-ea" - -Java(TM) SE Runtime Environment (build 1.8.0-ea-lambda-nightly-h109-20130902-b106-b00) - -Java HotSpot(TM) 64-Bit Server VM (build 25.0-b45, mixed mode) +java version "1.8.0_05" +Java(TM) SE Runtime Environment (build 1.8.0_05-b13) +Java HotSpot(TM) 64-Bit Server VM (build 25.5-b02, mixed mode) You can download a preview version here: https://jdk8.java.net/ @@ -47,3 +52,8 @@ $ java lambdasinaction/chap1/FilteringApples Alternatively you can compile the files manually inside the directory src/main/java + +You can also import the project in your favorite IDE: + * In IntelliJ use "File->Open" menu and navigate to the folder where the project resides + * In Eclipse use "File->Import->Existing Maven Projects" (also modify "Reduntant super interfaces" to report as Warnings instead of Errors + * In Netbeans use "File->Open Project" menu \ No newline at end of file diff --git a/pom.xml b/pom.xml index 32ca0b3e..10e5035e 100644 --- a/pom.xml +++ b/pom.xml @@ -13,6 +13,16 @@ + + org.openjdk.jmh + jmh-core + 1.17.4 + + + org.openjdk.jmh + jmh-generator-annprocess + 1.17.4 + junit junit @@ -27,11 +37,30 @@ maven-compiler-plugin 3.1 - 1.8 - 1.8 + 1.9 + 1.9 + + org.apache.maven.plugins + maven-shade-plugin + + + package + + shade + + + benchmarks + + + org.openjdk.jmh.Main + + + + + + - \ No newline at end of file diff --git a/src/main/java/lambdasinaction/appa/Author.java b/src/main/java/lambdasinaction/appa/Author.java new file mode 100644 index 00000000..a99235b9 --- /dev/null +++ b/src/main/java/lambdasinaction/appa/Author.java @@ -0,0 +1,13 @@ +package lambdasinaction.appa; + +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Repeatable(Authors.class) +@Retention(RetentionPolicy.RUNTIME) +public @interface Author { + + String name(); + +} diff --git a/src/main/java/lambdasinaction/appa/Authors.java b/src/main/java/lambdasinaction/appa/Authors.java new file mode 100644 index 00000000..f015d102 --- /dev/null +++ b/src/main/java/lambdasinaction/appa/Authors.java @@ -0,0 +1,11 @@ +package lambdasinaction.appa; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Authors { + + Author[] value(); + +} diff --git a/src/main/java/lambdasinaction/appa/Book.java b/src/main/java/lambdasinaction/appa/Book.java new file mode 100644 index 00000000..beefd807 --- /dev/null +++ b/src/main/java/lambdasinaction/appa/Book.java @@ -0,0 +1,17 @@ +package lambdasinaction.appa; + +import java.util.Arrays; + +@Author(name = "Raoul") +@Author(name = "Mario") +@Author(name = "Alan") +public class Book { + + public static void main(String[] args) { + Author[] authors = Book.class.getAnnotationsByType(Author.class); + Arrays.asList(authors).stream().forEach(a -> { + System.out.println(a.name()); + }); + } + +} diff --git a/src/main/java/lambdasinaction/chap6/StreamForker.java b/src/main/java/lambdasinaction/appc/StreamForker.java similarity index 99% rename from src/main/java/lambdasinaction/chap6/StreamForker.java rename to src/main/java/lambdasinaction/appc/StreamForker.java index f184e251..d8ed124c 100644 --- a/src/main/java/lambdasinaction/chap6/StreamForker.java +++ b/src/main/java/lambdasinaction/appc/StreamForker.java @@ -1,4 +1,4 @@ -package lambdasinaction.chap6; +package lambdasinaction.appc; import java.util.*; import java.util.concurrent.*; diff --git a/src/main/java/lambdasinaction/chap6/StreamForkerExample.java b/src/main/java/lambdasinaction/appc/StreamForkerExample.java similarity index 92% rename from src/main/java/lambdasinaction/chap6/StreamForkerExample.java rename to src/main/java/lambdasinaction/appc/StreamForkerExample.java index 551f816c..1d1ffdbc 100644 --- a/src/main/java/lambdasinaction/chap6/StreamForkerExample.java +++ b/src/main/java/lambdasinaction/appc/StreamForkerExample.java @@ -1,9 +1,9 @@ -package lambdasinaction.chap6; +package lambdasinaction.appc; -import lambdasinaction.chap5.*; +import lambdasinaction.chap6.*; import static java.util.stream.Collectors.*; -import static lambdasinaction.chap5.Dish.menu; +import static lambdasinaction.chap6.Dish.menu; import java.util.*; import java.util.stream.*; diff --git a/src/main/java/lambdasinaction/appd/InnerClass.java b/src/main/java/lambdasinaction/appd/InnerClass.java new file mode 100644 index 00000000..404b7b08 --- /dev/null +++ b/src/main/java/lambdasinaction/appd/InnerClass.java @@ -0,0 +1,12 @@ +package lambdasinaction.appd; + +import java.util.function.Function; + +public class InnerClass { + Function f = new Function() { + @Override + public String apply(Object obj) { + return obj.toString(); + } + }; +} diff --git a/src/main/java/lambdasinaction/appd/Lambda.java b/src/main/java/lambdasinaction/appd/Lambda.java new file mode 100644 index 00000000..e8381505 --- /dev/null +++ b/src/main/java/lambdasinaction/appd/Lambda.java @@ -0,0 +1,7 @@ +package lambdasinaction.appd; + +import java.util.function.Function; + +public class Lambda { + Function f = obj -> obj.toString(); +} diff --git a/src/main/java/lambdasinaction/chap9/Car.java b/src/main/java/lambdasinaction/chap10/Car.java similarity index 84% rename from src/main/java/lambdasinaction/chap9/Car.java rename to src/main/java/lambdasinaction/chap10/Car.java index 1b0f77db..36f00727 100644 --- a/src/main/java/lambdasinaction/chap9/Car.java +++ b/src/main/java/lambdasinaction/chap10/Car.java @@ -1,4 +1,4 @@ -package lambdasinaction.chap9; +package lambdasinaction.chap10; import java.util.*; diff --git a/src/main/java/lambdasinaction/chap9/Insurance.java b/src/main/java/lambdasinaction/chap10/Insurance.java similarity index 77% rename from src/main/java/lambdasinaction/chap9/Insurance.java rename to src/main/java/lambdasinaction/chap10/Insurance.java index d48eb927..69409686 100644 --- a/src/main/java/lambdasinaction/chap9/Insurance.java +++ b/src/main/java/lambdasinaction/chap10/Insurance.java @@ -1,4 +1,4 @@ -package lambdasinaction.chap9; +package lambdasinaction.chap10; public class Insurance { diff --git a/src/main/java/lambdasinaction/chap10/OperationsWithOptional.java b/src/main/java/lambdasinaction/chap10/OperationsWithOptional.java new file mode 100644 index 00000000..d2940969 --- /dev/null +++ b/src/main/java/lambdasinaction/chap10/OperationsWithOptional.java @@ -0,0 +1,25 @@ +package lambdasinaction.chap10; + +import java.util.*; + +import static java.util.Optional.of; +import static java.util.Optional.empty; + +public class OperationsWithOptional { + + public static void main(String... args) { + System.out.println(max(of(3), of(5))); + System.out.println(max(empty(), of(5))); + + Optional opt1 = of(5); + Optional opt2 = opt1.or(() -> of(4)); + + System.out.println( + of(5).or(() -> of(4)) + ); + } + + public static final Optional max(Optional i, Optional j) { + return i.flatMap(a -> j.map(b -> Math.max(a, b))); + } +} diff --git a/src/main/java/lambdasinaction/chap10/OptionalMain.java b/src/main/java/lambdasinaction/chap10/OptionalMain.java new file mode 100644 index 00000000..dcd97792 --- /dev/null +++ b/src/main/java/lambdasinaction/chap10/OptionalMain.java @@ -0,0 +1,24 @@ +package lambdasinaction.chap10; + +import java.util.*; + +import static java.util.stream.Collectors.toSet; + +public class OptionalMain { + + public String getCarInsuranceName(Optional person) { + return person.flatMap(Person::getCar) + .flatMap(Car::getInsurance) + .map(Insurance::getName) + .orElse("Unknown"); + } + + public Set getCarInsuranceNames(List persons) { + return persons.stream() + .map(Person::getCar) + .map(optCar -> optCar.flatMap(Car::getInsurance)) + .map(optInsurance -> optInsurance.map(Insurance::getName)) + .flatMap(Optional::stream) + .collect(toSet()); + } +} diff --git a/src/main/java/lambdasinaction/chap9/Person.java b/src/main/java/lambdasinaction/chap10/Person.java similarity index 81% rename from src/main/java/lambdasinaction/chap9/Person.java rename to src/main/java/lambdasinaction/chap10/Person.java index eb4d3726..5e84e552 100644 --- a/src/main/java/lambdasinaction/chap9/Person.java +++ b/src/main/java/lambdasinaction/chap10/Person.java @@ -1,4 +1,4 @@ -package lambdasinaction.chap9; +package lambdasinaction.chap10; import java.util.*; diff --git a/src/main/java/lambdasinaction/chap9/ReadPositiveIntParam.java b/src/main/java/lambdasinaction/chap10/ReadPositiveIntParam.java similarity index 98% rename from src/main/java/lambdasinaction/chap9/ReadPositiveIntParam.java rename to src/main/java/lambdasinaction/chap10/ReadPositiveIntParam.java index ee2cf434..acac12b9 100644 --- a/src/main/java/lambdasinaction/chap9/ReadPositiveIntParam.java +++ b/src/main/java/lambdasinaction/chap10/ReadPositiveIntParam.java @@ -1,4 +1,4 @@ -package lambdasinaction.chap9; +package lambdasinaction.chap10; import org.junit.*; diff --git a/src/main/java/lambdasinaction/chap11/AsyncShop.java b/src/main/java/lambdasinaction/chap11/AsyncShop.java new file mode 100644 index 00000000..9d537368 --- /dev/null +++ b/src/main/java/lambdasinaction/chap11/AsyncShop.java @@ -0,0 +1,42 @@ +package lambdasinaction.chap11; + +import static lambdasinaction.chap11.Util.delay; +import static lambdasinaction.chap11.Util.format; + +import java.util.Random; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; + +public class AsyncShop { + + private final String name; + private final Random random; + + public AsyncShop(String name) { + this.name = name; + random = new Random(name.charAt(0) * name.charAt(1) * name.charAt(2)); + } + + public Future getPrice(String product) { +/* + CompletableFuture futurePrice = new CompletableFuture<>(); + new Thread( () -> { + try { + double price = calculatePrice(product); + futurePrice.complete(price); + } catch (Exception ex) { + futurePrice.completeExceptionally(ex); + } + }).start(); + return futurePrice; +*/ + return CompletableFuture.supplyAsync(() -> calculatePrice(product)); + } + + private double calculatePrice(String product) { + delay(); + if (true) throw new RuntimeException("product not available"); + return format(random.nextDouble() * product.charAt(0) + product.charAt(1)); + } + +} \ No newline at end of file diff --git a/src/main/java/lambdasinaction/chap11/AsyncShopClient.java b/src/main/java/lambdasinaction/chap11/AsyncShopClient.java new file mode 100644 index 00000000..9a78f827 --- /dev/null +++ b/src/main/java/lambdasinaction/chap11/AsyncShopClient.java @@ -0,0 +1,21 @@ +package lambdasinaction.chap11; + +import java.util.concurrent.Future; + +public class AsyncShopClient { + + public static void main(String[] args) { + AsyncShop shop = new AsyncShop("BestShop"); + long start = System.nanoTime(); + Future futurePrice = shop.getPrice("myPhone"); + long incocationTime = ((System.nanoTime() - start) / 1_000_000); + System.out.println("Invocation returned after " + incocationTime + " msecs"); + try { + System.out.println("Price is " + futurePrice.get()); + } catch (Exception e) { + throw new RuntimeException(e); + } + long retrivalTime = ((System.nanoTime() - start) / 1_000_000); + System.out.println("Price returned after " + retrivalTime + " msecs"); + } +} \ No newline at end of file diff --git a/src/main/java/lambdasinaction/chap11/BestPriceFinder.java b/src/main/java/lambdasinaction/chap11/BestPriceFinder.java new file mode 100644 index 00000000..51412e93 --- /dev/null +++ b/src/main/java/lambdasinaction/chap11/BestPriceFinder.java @@ -0,0 +1,70 @@ +package lambdasinaction.chap11; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class BestPriceFinder { + + private final List shops = Arrays.asList(new Shop("BestPrice"), + new Shop("LetsSaveBig"), + new Shop("MyFavoriteShop"), + new Shop("BuyItAll"), + new Shop("ShopEasy")); + + private final Executor executor = Executors.newFixedThreadPool(shops.size(), new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setDaemon(true); + return t; + } + }); + + public List findPricesSequential(String product) { + return shops.stream() + .map(shop -> shop.getPrice(product)) + .map(Quote::parse) + .map(Discount::applyDiscount) + .collect(Collectors.toList()); + } + + public List findPricesParallel(String product) { + return shops.parallelStream() + .map(shop -> shop.getPrice(product)) + .map(Quote::parse) + .map(Discount::applyDiscount) + .collect(Collectors.toList()); + } + + public List findPricesFuture(String product) { + List> priceFutures = findPricesStream(product) + .collect(Collectors.>toList()); + + return priceFutures.stream() + .map(CompletableFuture::join) + .collect(Collectors.toList()); + } + + public Stream> findPricesStream(String product) { + return shops.stream() + .map(shop -> CompletableFuture.supplyAsync(() -> shop.getPrice(product), executor)) + .map(future -> future.thenApply(Quote::parse)) + .map(future -> future.thenCompose(quote -> CompletableFuture.supplyAsync(() -> Discount.applyDiscount(quote), executor))); + } + + public void printPricesStream(String product) { + long start = System.nanoTime(); + CompletableFuture[] futures = findPricesStream(product) + .map(f -> f.thenAccept(s -> System.out.println(s + " (done in " + ((System.nanoTime() - start) / 1_000_000) + " msecs)"))) + .toArray(size -> new CompletableFuture[size]); + CompletableFuture.allOf(futures).join(); + System.out.println("All shops have now responded in " + ((System.nanoTime() - start) / 1_000_000) + " msecs"); + } + +} diff --git a/src/main/java/lambdasinaction/chap11/BestPriceFinderMain.java b/src/main/java/lambdasinaction/chap11/BestPriceFinderMain.java new file mode 100644 index 00000000..43ec8fba --- /dev/null +++ b/src/main/java/lambdasinaction/chap11/BestPriceFinderMain.java @@ -0,0 +1,24 @@ +package lambdasinaction.chap11; + +import java.util.List; +import java.util.function.Supplier; + +public class BestPriceFinderMain { + + private static BestPriceFinder bestPriceFinder = new BestPriceFinder(); + + public static void main(String[] args) { + execute("sequential", () -> bestPriceFinder.findPricesSequential("myPhone27S")); + execute("parallel", () -> bestPriceFinder.findPricesParallel("myPhone27S")); + execute("composed CompletableFuture", () -> bestPriceFinder.findPricesFuture("myPhone27S")); + bestPriceFinder.printPricesStream("myPhone27S"); + } + + private static void execute(String msg, Supplier> s) { + long start = System.nanoTime(); + System.out.println(s.get()); + long duration = (System.nanoTime() - start) / 1_000_000; + System.out.println(msg + " done in " + duration + " msecs"); + } + +} diff --git a/src/main/java/lambdasinaction/chap11/Discount.java b/src/main/java/lambdasinaction/chap11/Discount.java new file mode 100644 index 00000000..5624abd1 --- /dev/null +++ b/src/main/java/lambdasinaction/chap11/Discount.java @@ -0,0 +1,25 @@ +package lambdasinaction.chap11; + +import static lambdasinaction.chap11.Util.delay; +import static lambdasinaction.chap11.Util.format; + +public class Discount { + + public enum Code { + NONE(0), SILVER(5), GOLD(10), PLATINUM(15), DIAMOND(20); + + private final int percentage; + + Code(int percentage) { + this.percentage = percentage; + } + } + + public static String applyDiscount(Quote quote) { + return quote.getShopName() + " price is " + Discount.apply(quote.getPrice(), quote.getDiscountCode()); + } + private static double apply(double price, Code code) { + delay(); + return format(price * (100 - code.percentage) / 100); + } +} diff --git a/src/main/java/lambdasinaction/chap11/ExchangeService.java b/src/main/java/lambdasinaction/chap11/ExchangeService.java new file mode 100644 index 00000000..3f98a7e3 --- /dev/null +++ b/src/main/java/lambdasinaction/chap11/ExchangeService.java @@ -0,0 +1,26 @@ +package lambdasinaction.chap11; + +import static lambdasinaction.chap11.Util.delay; + +public class ExchangeService { + + public enum Money { + USD(1.0), EUR(1.35387), GBP(1.69715), CAD(.92106), MXN(.07683); + + private final double rate; + + Money(double rate) { + this.rate = rate; + } + } + + public static double getRate(Money source, Money destination) { + return getRateWithDelay(source, destination); + } + + private static double getRateWithDelay(Money source, Money destination) { + delay(); + return destination.rate / source.rate; + } + +} diff --git a/src/main/java/lambdasinaction/chap11/Quote.java b/src/main/java/lambdasinaction/chap11/Quote.java new file mode 100644 index 00000000..72dd81bf --- /dev/null +++ b/src/main/java/lambdasinaction/chap11/Quote.java @@ -0,0 +1,34 @@ +package lambdasinaction.chap11; + +public class Quote { + + private final String shopName; + private final double price; + private final Discount.Code discountCode; + + public Quote(String shopName, double price, Discount.Code discountCode) { + this.shopName = shopName; + this.price = price; + this.discountCode = discountCode; + } + + public static Quote parse(String s) { + String[] split = s.split(":"); + String shopName = split[0]; + double price = Double.parseDouble(split[1]); + Discount.Code discountCode = Discount.Code.valueOf(split[2]); + return new Quote(shopName, price, discountCode); + } + + public String getShopName() { + return shopName; + } + + public double getPrice() { + return price; + } + + public Discount.Code getDiscountCode() { + return discountCode; + } +} diff --git a/src/main/java/lambdasinaction/chap11/Shop.java b/src/main/java/lambdasinaction/chap11/Shop.java new file mode 100644 index 00000000..bf0446fe --- /dev/null +++ b/src/main/java/lambdasinaction/chap11/Shop.java @@ -0,0 +1,32 @@ +package lambdasinaction.chap11; + +import static lambdasinaction.chap11.Util.delay; +import static lambdasinaction.chap11.Util.format; + +import java.util.Random; + +public class Shop { + + private final String name; + private final Random random; + + public Shop(String name) { + this.name = name; + random = new Random(name.charAt(0) * name.charAt(1) * name.charAt(2)); + } + + public String getPrice(String product) { + double price = calculatePrice(product); + Discount.Code code = Discount.Code.values()[random.nextInt(Discount.Code.values().length)]; + return name + ":" + price + ":" + code; + } + + public double calculatePrice(String product) { + delay(); + return format(random.nextDouble() * product.charAt(0) + product.charAt(1)); + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/lambdasinaction/chap11/Util.java b/src/main/java/lambdasinaction/chap11/Util.java new file mode 100644 index 00000000..a72c4d33 --- /dev/null +++ b/src/main/java/lambdasinaction/chap11/Util.java @@ -0,0 +1,46 @@ +package lambdasinaction.chap11; + +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.List; +import java.util.Locale; +import java.util.Random; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +public class Util { + + private static final Random RANDOM = new Random(0); + private static final DecimalFormat formatter = new DecimalFormat("#.##", new DecimalFormatSymbols(Locale.US)); + + public static void delay() { + int delay = 1000; + //int delay = 500 + RANDOM.nextInt(2000); + try { + Thread.sleep(delay); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + public static double format(double number) { + synchronized (formatter) { + return new Double(formatter.format(number)); + } + } + + public static CompletableFuture> sequence(List> futures) { +/* + CompletableFuture allDoneFuture = + CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])); + return allDoneFuture.thenApply(v -> + futures.stream(). + map(future -> future.join()). + collect(Collectors.toList()) + ); +*/ + return CompletableFuture.supplyAsync(() -> futures.stream(). + map(future -> future.join()). + collect(Collectors.toList())); + } +} diff --git a/src/main/java/lambdasinaction/chap11/v1/BestPriceFinder.java b/src/main/java/lambdasinaction/chap11/v1/BestPriceFinder.java new file mode 100644 index 00000000..1bbb6030 --- /dev/null +++ b/src/main/java/lambdasinaction/chap11/v1/BestPriceFinder.java @@ -0,0 +1,163 @@ +package lambdasinaction.chap11.v1; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.ThreadFactory; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import lambdasinaction.chap11.ExchangeService; +import lambdasinaction.chap11.ExchangeService.Money; + +public class BestPriceFinder { + + private final List shops = Arrays.asList(new Shop("BestPrice"), + new Shop("LetsSaveBig"), + new Shop("MyFavoriteShop"), + new Shop("BuyItAll")/*, + new Shop("ShopEasy")*/); + + private final Executor executor = Executors.newFixedThreadPool(shops.size(), new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setDaemon(true); + return t; + } + }); + + public List findPricesSequential(String product) { + return shops.stream() + .map(shop -> shop.getName() + " price is " + shop.getPrice(product)) + .collect(Collectors.toList()); + } + + public List findPricesParallel(String product) { + return shops.parallelStream() + .map(shop -> shop.getName() + " price is " + shop.getPrice(product)) + .collect(Collectors.toList()); + } + + public List findPricesFuture(String product) { + List> priceFutures = + shops.stream() + .map(shop -> CompletableFuture.supplyAsync(() -> shop.getName() + " price is " + + shop.getPrice(product), executor)) + .collect(Collectors.toList()); + + List prices = priceFutures.stream() + .map(CompletableFuture::join) + .collect(Collectors.toList()); + return prices; + } + + public List findPricesInUSD(String product) { + List> priceFutures = new ArrayList<>(); + for (Shop shop : shops) { + // Start of Listing 10.20. + // Only the type of futurePriceInUSD has been changed to + // CompletableFuture so that it is compatible with the + // CompletableFuture::join operation below. + CompletableFuture futurePriceInUSD = + CompletableFuture.supplyAsync(() -> shop.getPrice(product)) + .thenCombine( + CompletableFuture.supplyAsync( + () -> ExchangeService.getRate(Money.EUR, Money.USD)), + (price, rate) -> price * rate + ); + priceFutures.add(futurePriceInUSD); + } + // Drawback: The shop is not accessible anymore outside the loop, + // so the getName() call below has been commented out. + List prices = priceFutures + .stream() + .map(CompletableFuture::join) + .map(price -> /*shop.getName() +*/ " price is " + price) + .collect(Collectors.toList()); + return prices; + } + + public List findPricesInUSDJava7(String product) { + ExecutorService executor = Executors.newCachedThreadPool(); + List> priceFutures = new ArrayList<>(); + for (Shop shop : shops) { + final Future futureRate = executor.submit(new Callable() { + public Double call() { + return ExchangeService.getRate(Money.EUR, Money.USD); + } + }); + Future futurePriceInUSD = executor.submit(new Callable() { + public Double call() { + try { + double priceInEUR = shop.getPrice(product); + return priceInEUR * futureRate.get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e.getMessage(), e); + } + } + }); + priceFutures.add(futurePriceInUSD); + } + List prices = new ArrayList<>(); + for (Future priceFuture : priceFutures) { + try { + prices.add(/*shop.getName() +*/ " price is " + priceFuture.get()); + } + catch (ExecutionException | InterruptedException e) { + e.printStackTrace(); + } + } + return prices; + } + + public List findPricesInUSD2(String product) { + List> priceFutures = new ArrayList<>(); + for (Shop shop : shops) { + // Here, an extra operation has been added so that the shop name + // is retrieved within the loop. As a result, we now deal with + // CompletableFuture instances. + CompletableFuture futurePriceInUSD = + CompletableFuture.supplyAsync(() -> shop.getPrice(product)) + .thenCombine( + CompletableFuture.supplyAsync( + () -> ExchangeService.getRate(Money.EUR, Money.USD)), + (price, rate) -> price * rate + ).thenApply(price -> shop.getName() + " price is " + price); + priceFutures.add(futurePriceInUSD); + } + List prices = priceFutures + .stream() + .map(CompletableFuture::join) + .collect(Collectors.toList()); + return prices; + } + + public List findPricesInUSD3(String product) { + // Here, the for loop has been replaced by a mapping function... + Stream> priceFuturesStream = shops + .stream() + .map(shop -> CompletableFuture + .supplyAsync(() -> shop.getPrice(product)) + .thenCombine( + CompletableFuture.supplyAsync(() -> ExchangeService.getRate(Money.EUR, Money.USD)), + (price, rate) -> price * rate) + .thenApply(price -> shop.getName() + " price is " + price)); + // However, we should gather the CompletableFutures into a List so that the asynchronous + // operations are triggered before being "joined." + List> priceFutures = priceFuturesStream.collect(Collectors.toList()); + List prices = priceFutures + .stream() + .map(CompletableFuture::join) + .collect(Collectors.toList()); + return prices; + } + +} diff --git a/src/main/java/lambdasinaction/chap11/v1/BestPriceFinderMain.java b/src/main/java/lambdasinaction/chap11/v1/BestPriceFinderMain.java new file mode 100644 index 00000000..5a74161a --- /dev/null +++ b/src/main/java/lambdasinaction/chap11/v1/BestPriceFinderMain.java @@ -0,0 +1,25 @@ +package lambdasinaction.chap11.v1; + +import java.util.List; +import java.util.function.Supplier; + +public class BestPriceFinderMain { + + private static BestPriceFinder bestPriceFinder = new BestPriceFinder(); + + public static void main(String[] args) { + execute("sequential", () -> bestPriceFinder.findPricesSequential("myPhone27S")); + execute("parallel", () -> bestPriceFinder.findPricesParallel("myPhone27S")); + execute("composed CompletableFuture", () -> bestPriceFinder.findPricesFuture("myPhone27S")); + execute("combined USD CompletableFuture", () -> bestPriceFinder.findPricesInUSD("myPhone27S")); + execute("combined USD CompletableFuture v2", () -> bestPriceFinder.findPricesInUSD2("myPhone27S")); + execute("combined USD CompletableFuture v3", () -> bestPriceFinder.findPricesInUSD3("myPhone27S")); + } + + private static void execute(String msg, Supplier> s) { + long start = System.nanoTime(); + System.out.println(s.get()); + long duration = (System.nanoTime() - start) / 1_000_000; + System.out.println(msg + " done in " + duration + " msecs"); + } +} diff --git a/src/main/java/lambdasinaction/chap11/v1/Shop.java b/src/main/java/lambdasinaction/chap11/v1/Shop.java new file mode 100644 index 00000000..cd3da975 --- /dev/null +++ b/src/main/java/lambdasinaction/chap11/v1/Shop.java @@ -0,0 +1,41 @@ +package lambdasinaction.chap11.v1; + +import static lambdasinaction.chap11.Util.delay; + +import java.util.Random; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; + +public class Shop { + + private final String name; + private final Random random; + + public Shop(String name) { + this.name = name; + random = new Random(name.charAt(0) * name.charAt(1) * name.charAt(2)); + } + + public double getPrice(String product) { + return calculatePrice(product); + } + + private double calculatePrice(String product) { + delay(); + return random.nextDouble() * product.charAt(0) + product.charAt(1); + } + + public Future getPriceAsync(String product) { + CompletableFuture futurePrice = new CompletableFuture<>(); + new Thread( () -> { + double price = calculatePrice(product); + futurePrice.complete(price); + }).start(); + return futurePrice; + } + + public String getName() { + return name; + } + +} diff --git a/src/main/java/lambdasinaction/chap11/v1/ShopMain.java b/src/main/java/lambdasinaction/chap11/v1/ShopMain.java new file mode 100644 index 00000000..ea1a19c1 --- /dev/null +++ b/src/main/java/lambdasinaction/chap11/v1/ShopMain.java @@ -0,0 +1,32 @@ +package lambdasinaction.chap11.v1; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +public class ShopMain { + + public static void main(String[] args) { + Shop shop = new Shop("BestShop"); + long start = System.nanoTime(); + Future futurePrice = shop.getPriceAsync("my favorite product"); + long invocationTime = ((System.nanoTime() - start) / 1_000_000); + System.out.println("Invocation returned after " + invocationTime + + " msecs"); + // Do some more tasks, like querying other shops + doSomethingElse(); + // while the price of the product is being calculated + try { + double price = futurePrice.get(); + System.out.printf("Price is %.2f%n", price); + } catch (ExecutionException | InterruptedException e) { + throw new RuntimeException(e); + } + long retrievalTime = ((System.nanoTime() - start) / 1_000_000); + System.out.println("Price returned after " + retrievalTime + " msecs"); + } + + private static void doSomethingElse() { + System.out.println("Doing something else..."); + } + +} diff --git a/src/main/java/lambdasinaction/chap12/DateTimeExamples.java b/src/main/java/lambdasinaction/chap12/DateTimeExamples.java new file mode 100644 index 00000000..46895cc5 --- /dev/null +++ b/src/main/java/lambdasinaction/chap12/DateTimeExamples.java @@ -0,0 +1,157 @@ +package lambdasinaction.chap12; + +import static java.time.temporal.TemporalAdjusters.lastDayOfMonth; +import static java.time.temporal.TemporalAdjusters.nextOrSame; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.time.DayOfWeek; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.Month; +import java.time.chrono.JapaneseDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.temporal.ChronoField; +import java.time.temporal.ChronoUnit; +import java.time.temporal.Temporal; +import java.time.temporal.TemporalAdjuster; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; + +public class DateTimeExamples { + + private static final ThreadLocal formatters = new ThreadLocal() { + protected DateFormat initialValue() { + return new SimpleDateFormat("dd-MMM-yyyy"); + } + }; + + public static void main(String[] args) { + useOldDate(); + useLocalDate(); + useTemporalAdjuster(); + useDateFormatter(); + } + + private static void useOldDate() { + Date date = new Date(114, 2, 18); + System.out.println(date); + + System.out.println(formatters.get().format(date)); + + Calendar calendar = Calendar.getInstance(); + calendar.set(2014, Calendar.FEBRUARY, 18); + System.out.println(calendar); + } + + private static void useLocalDate() { + LocalDate date = LocalDate.of(2014, 3, 18); + int year = date.getYear(); // 2014 + Month month = date.getMonth(); // MARCH + int day = date.getDayOfMonth(); // 18 + DayOfWeek dow = date.getDayOfWeek(); // TUESDAY + int len = date.lengthOfMonth(); // 31 (days in March) + boolean leap = date.isLeapYear(); // false (not a leap year) + System.out.println(date); + + int y = date.get(ChronoField.YEAR); + int m = date.get(ChronoField.MONTH_OF_YEAR); + int d = date.get(ChronoField.DAY_OF_MONTH); + + LocalTime time = LocalTime.of(13, 45, 20); // 13:45:20 + int hour = time.getHour(); // 13 + int minute = time.getMinute(); // 45 + int second = time.getSecond(); // 20 + System.out.println(time); + + LocalDateTime dt1 = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45, 20); // 2014-03-18T13:45 + LocalDateTime dt2 = LocalDateTime.of(date, time); + LocalDateTime dt3 = date.atTime(13, 45, 20); + LocalDateTime dt4 = date.atTime(time); + LocalDateTime dt5 = time.atDate(date); + System.out.println(dt1); + + LocalDate date1 = dt1.toLocalDate(); + System.out.println(date1); + LocalTime time1 = dt1.toLocalTime(); + System.out.println(time1); + + Instant instant = Instant.ofEpochSecond(44 * 365 * 86400); + Instant now = Instant.now(); + + Duration d1 = Duration.between(LocalTime.of(13, 45, 10), time); + Duration d2 = Duration.between(instant, now); + System.out.println(d1.getSeconds()); + System.out.println(d2.getSeconds()); + + Duration threeMinutes = Duration.of(3, ChronoUnit.MINUTES); + System.out.println(threeMinutes); + + JapaneseDate japaneseDate = JapaneseDate.from(date); + System.out.println(japaneseDate); + } + + private static void useTemporalAdjuster() { + LocalDate date = LocalDate.of(2014, 3, 18); + date = date.with(nextOrSame(DayOfWeek.SUNDAY)); + System.out.println(date); + date = date.with(lastDayOfMonth()); + System.out.println(date); + + date = date.with(new NextWorkingDay()); + System.out.println(date); + date = date.with(nextOrSame(DayOfWeek.FRIDAY)); + System.out.println(date); + date = date.with(new NextWorkingDay()); + System.out.println(date); + + date = date.with(nextOrSame(DayOfWeek.FRIDAY)); + System.out.println(date); + date = date.with(temporal -> { + DayOfWeek dow = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK)); + int dayToAdd = 1; + if (dow == DayOfWeek.FRIDAY) dayToAdd = 3; + if (dow == DayOfWeek.SATURDAY) dayToAdd = 2; + return temporal.plus(dayToAdd, ChronoUnit.DAYS); + }); + System.out.println(date); + } + + private static class NextWorkingDay implements TemporalAdjuster { + @Override + public Temporal adjustInto(Temporal temporal) { + DayOfWeek dow = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK)); + int dayToAdd = 1; + if (dow == DayOfWeek.FRIDAY) dayToAdd = 3; + if (dow == DayOfWeek.SATURDAY) dayToAdd = 2; + return temporal.plus(dayToAdd, ChronoUnit.DAYS); + } + } + + private static void useDateFormatter() { + LocalDate date = LocalDate.of(2014, 3, 18); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy"); + DateTimeFormatter italianFormatter = DateTimeFormatter.ofPattern("d. MMMM yyyy", Locale.ITALIAN); + + System.out.println(date.format(DateTimeFormatter.ISO_LOCAL_DATE)); + System.out.println(date.format(formatter)); + System.out.println(date.format(italianFormatter)); + + DateTimeFormatter complexFormatter = new DateTimeFormatterBuilder() + .appendText(ChronoField.DAY_OF_MONTH) + .appendLiteral(". ") + .appendText(ChronoField.MONTH_OF_YEAR) + .appendLiteral(" ") + .appendText(ChronoField.YEAR) + .parseCaseInsensitive() + .toFormatter(Locale.ITALIAN); + + System.out.println(date.format(complexFormatter)); + } + +} diff --git a/src/main/java/lambdasinaction/chap13/Recursion.java b/src/main/java/lambdasinaction/chap13/Recursion.java new file mode 100644 index 00000000..77f66d57 --- /dev/null +++ b/src/main/java/lambdasinaction/chap13/Recursion.java @@ -0,0 +1,39 @@ +package lambdasinaction.chap13; + +import java.util.stream.LongStream; + + +public class Recursion { + + public static void main(String[] args) { + System.out.println(factorialIterative(5)); + System.out.println(factorialRecursive(5)); + System.out.println(factorialStreams(5)); + System.out.println(factorialTailRecursive(5)); + } + + public static int factorialIterative(int n) { + int r = 1; + for (int i = 1; i <= n; i++) { + r*=i; + } + return r; + } + + public static long factorialRecursive(long n) { + return n == 1 ? 1 : n*factorialRecursive(n-1); + } + + public static long factorialStreams(long n){ + return LongStream.rangeClosed(1, n) + .reduce(1, (long a, long b) -> a * b); + } + + public static long factorialTailRecursive(long n) { + return factorialHelper(1, n); + } + + public static long factorialHelper(long acc, long n) { + return n == 1 ? acc : factorialHelper(acc * n, n-1); + } +} diff --git a/src/main/java/lambdasinaction/chap13/SubsetsMain.java b/src/main/java/lambdasinaction/chap13/SubsetsMain.java new file mode 100644 index 00000000..02b51998 --- /dev/null +++ b/src/main/java/lambdasinaction/chap13/SubsetsMain.java @@ -0,0 +1,44 @@ +package lambdasinaction.chap13; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class SubsetsMain { + + public static void main(String[] args) { + List> subs = subsets(Arrays.asList(1, 4, 9)); + subs.forEach(System.out::println); + } + + public static List> subsets(List l) { + if (l.isEmpty()) { + List> ans = new ArrayList<>(); + ans.add(Collections.emptyList()); + return ans; + } + Integer first = l.get(0); + List rest = l.subList(1,l.size()); + List> subans = subsets(rest); + List> subans2 = insertAll(first, subans); + return concat(subans, subans2); + } + + public static List> insertAll(Integer first, List> lists) { + List> result = new ArrayList<>(); + for (List l : lists) { + List copyList = new ArrayList<>(); + copyList.add(first); + copyList.addAll(l); + result.add(copyList); + } + return result; + } + + static List> concat(List> a, List> b) { + List> r = new ArrayList<>(a); + r.addAll(b); + return r; + } +} diff --git a/src/main/java/lambdasinaction/chap14/Combinators.java b/src/main/java/lambdasinaction/chap14/Combinators.java new file mode 100644 index 00000000..7ae5dfd2 --- /dev/null +++ b/src/main/java/lambdasinaction/chap14/Combinators.java @@ -0,0 +1,18 @@ +package lambdasinaction.chap14; + +import java.util.function.Function; + +public class Combinators { + + public static void main(String[] args) { + System.out.println(repeat(3, (Integer x) -> 2 * x).apply(10)); + } + + static Function compose(Function g, Function f) { + return x -> g.apply(f.apply(x)); + } + + static Function repeat(int n, Function f) { + return n == 0 ? x -> x : compose(f, repeat(n - 1, f)); + } +} diff --git a/src/main/java/lambdasinaction/chap14/Currying.java b/src/main/java/lambdasinaction/chap14/Currying.java new file mode 100644 index 00000000..f17ac928 --- /dev/null +++ b/src/main/java/lambdasinaction/chap14/Currying.java @@ -0,0 +1,32 @@ +package lambdasinaction.chap14; + +import java.util.function.DoubleUnaryOperator; + + +public class Currying { + + public static void main(String[] args) { + DoubleUnaryOperator convertCtoF = curriedConverter(9.0/5, 32); + DoubleUnaryOperator convertUSDtoGBP = curriedConverter(0.6, 0); + DoubleUnaryOperator convertKmtoMi = curriedConverter(0.6214, 0); + + System.out.println(convertCtoF.applyAsDouble(24)); + System.out.println(convertUSDtoGBP.applyAsDouble(100)); + System.out.println(convertKmtoMi.applyAsDouble(20)); + + DoubleUnaryOperator convertFtoC = expandedCurriedConverter(-32, 5.0/9, 0); + System.out.println(convertFtoC.applyAsDouble(98.6)); + } + + static double converter(double x, double y, double z) { + return x * y + z; + } + + static DoubleUnaryOperator curriedConverter(double y, double z) { + return (double x) -> x * y + z; + } + + static DoubleUnaryOperator expandedCurriedConverter(double w, double y, double z) { + return (double x) -> (x + w) * y + z; + } +} diff --git a/src/main/java/lambdasinaction/chap14/LazyLists.java b/src/main/java/lambdasinaction/chap14/LazyLists.java new file mode 100644 index 00000000..658e0940 --- /dev/null +++ b/src/main/java/lambdasinaction/chap14/LazyLists.java @@ -0,0 +1,129 @@ +package lambdasinaction.chap14; + +import java.util.function.Supplier; +import java.util.function.Predicate; + +public class LazyLists { + + public static void main(String[] args) { + MyList l = new MyLinkedList<>(5, new MyLinkedList<>(10, + new Empty())); + + System.out.println(l.head()); + + LazyList numbers = from(2); + int two = numbers.head(); + int three = numbers.tail().head(); + int four = numbers.tail().tail().head(); + System.out.println(two + " " + three + " " + four); + + numbers = from(2); + int prime_two = primes(numbers).head(); + int prime_three = primes(numbers).tail().head(); + int prime_five = primes(numbers).tail().tail().head(); + System.out.println(prime_two + " " + prime_three + " " + prime_five); + + // this will run until a stackoverflow occur because Java does not + // support tail call elimination + // printAll(primes(from(2))); + } + + interface MyList { + T head(); + + MyList tail(); + + default boolean isEmpty() { + return true; + } + + MyList filter(Predicate p); + } + + static class MyLinkedList implements MyList { + final T head; + final MyList tail; + + public MyLinkedList(T head, MyList tail) { + this.head = head; + this.tail = tail; + } + + public T head() { + return head; + } + + public MyList tail() { + return tail; + } + + public boolean isEmpty() { + return false; + } + + public MyList filter(Predicate p) { + return isEmpty() ? this : p.test(head()) ? new MyLinkedList<>( + head(), tail().filter(p)) : tail().filter(p); + } + } + + static class Empty implements MyList { + public T head() { + throw new UnsupportedOperationException(); + } + + public MyList tail() { + throw new UnsupportedOperationException(); + } + + public MyList filter(Predicate p) { + return this; + } + } + + static class LazyList implements MyList { + final T head; + final Supplier> tail; + + public LazyList(T head, Supplier> tail) { + this.head = head; + this.tail = tail; + } + + public T head() { + return head; + } + + public MyList tail() { + return tail.get(); + } + + public boolean isEmpty() { + return false; + } + + public MyList filter(Predicate p) { + return isEmpty() ? this : p.test(head()) ? new LazyList<>(head(), + () -> tail().filter(p)) : tail().filter(p); + } + + } + + public static LazyList from(int n) { + return new LazyList(n, () -> from(n + 1)); + } + + public static MyList primes(MyList numbers) { + return new LazyList<>(numbers.head(), () -> primes(numbers.tail() + .filter(n -> n % numbers.head() != 0))); + } + + static void printAll(MyList numbers) { + if (numbers.isEmpty()) { + return; + } + System.out.println(numbers.head()); + printAll(numbers.tail()); + } + +} diff --git a/src/main/java/lambdasinaction/chap14/PatternMatching.java b/src/main/java/lambdasinaction/chap14/PatternMatching.java new file mode 100644 index 00000000..400593d4 --- /dev/null +++ b/src/main/java/lambdasinaction/chap14/PatternMatching.java @@ -0,0 +1,139 @@ +package lambdasinaction.chap14; + +import java.util.function.Function; +import java.util.function.Supplier; + +public class PatternMatching { + + public static void main(String[] args) { + simplify(); + + Expr e = new BinOp("+", new Number(5), new BinOp("*", new Number(3), new Number(4))); + Integer result = evaluate(e); + System.out.println(e + " = " + result); + } + + private static void simplify() { + TriFunction binopcase = + (opname, left, right) -> { + if ("+".equals(opname)) { + if (left instanceof Number && ((Number) left).val == 0) { + return right; + } + if (right instanceof Number && ((Number) right).val == 0) { + return left; + } + } + if ("*".equals(opname)) { + if (left instanceof Number && ((Number) left).val == 1) { + return right; + } + if (right instanceof Number && ((Number) right).val == 1) { + return left; + } + } + return new BinOp(opname, left, right); + }; + Function numcase = val -> new Number(val); + Supplier defaultcase = () -> new Number(0); + + Expr e = new BinOp("+", new Number(5), new Number(0)); + Expr match = patternMatchExpr(e, binopcase, numcase, defaultcase); + if (match instanceof Number) { + System.out.println("Number: " + match); + } else if (match instanceof BinOp) { + System.out.println("BinOp: " + match); + } + } + + private static Integer evaluate(Expr e) { + Function numcase = val -> val; + Supplier defaultcase = () -> 0; + TriFunction binopcase = + (opname, left, right) -> { + if ("+".equals(opname)) { + if (left instanceof Number && right instanceof Number) { + return ((Number) left).val + ((Number) right).val; + } + if (right instanceof Number && left instanceof BinOp) { + return ((Number) right).val + evaluate((BinOp) left); + } + if (left instanceof Number && right instanceof BinOp) { + return ((Number) left).val + evaluate((BinOp) right); + } + if (left instanceof BinOp && right instanceof BinOp) { + return evaluate((BinOp) left) + evaluate((BinOp) right); + } + } + if ("*".equals(opname)) { + if (left instanceof Number && right instanceof Number) { + return ((Number) left).val * ((Number) right).val; + } + if (right instanceof Number && left instanceof BinOp) { + return ((Number) right).val * evaluate((BinOp) left); + } + if (left instanceof Number && right instanceof BinOp) { + return ((Number) left).val * evaluate((BinOp) right); + } + if (left instanceof BinOp && right instanceof BinOp) { + return evaluate((BinOp) left) * evaluate((BinOp) right); + } + } + return defaultcase.get(); + }; + + return patternMatchExpr(e, binopcase, numcase, defaultcase); + } + + static class Expr { + } + + static class Number extends Expr { + int val; + public Number(int val) { + this.val = val; + } + + @Override + public String toString() { + return "" + val; + } + } + + static class BinOp extends Expr { + String opname; + Expr left, right; + public BinOp(String opname, Expr left, Expr right) { + this.opname = opname; + this.left = left; + this.right = right; + } + + @Override + public String toString() { + return "(" + left + " " + opname + " " + right + ")"; + } + } + + static T MyIf(boolean b, Supplier truecase, Supplier falsecase) { + return b ? truecase.get() : falsecase.get(); + } + + static interface TriFunction { + R apply(S s, T t, U u); + } + + static T patternMatchExpr(Expr e, + TriFunction binopcase, + Function numcase, Supplier defaultcase) { + + if (e instanceof BinOp) { + return binopcase.apply(((BinOp) e).opname, ((BinOp) e).left, ((BinOp) e).right); + } else if (e instanceof Number) { + return numcase.apply(((Number) e).val); + } else { + return defaultcase.get(); + } + } + +} diff --git a/src/main/java/lambdasinaction/chap14/PersistentTrainJourney.java b/src/main/java/lambdasinaction/chap14/PersistentTrainJourney.java new file mode 100644 index 00000000..7e958a14 --- /dev/null +++ b/src/main/java/lambdasinaction/chap14/PersistentTrainJourney.java @@ -0,0 +1,66 @@ +package lambdasinaction.chap14; + +import java.util.function.Consumer; + +public class PersistentTrainJourney { + + public static void main(String[] args) { + TrainJourney tj1 = new TrainJourney(40, new TrainJourney(30, null)); + TrainJourney tj2 = new TrainJourney(20, new TrainJourney(50, null)); + + TrainJourney appended = append(tj1, tj2); + visit(appended, tj -> { System.out.print(tj.price + " - "); }); + System.out.println(); + + // A new TrainJourney is created without altering tj1 and tj2. + TrainJourney appended2 = append(tj1, tj2); + visit(appended2, tj -> { System.out.print(tj.price + " - "); }); + System.out.println(); + + // tj1 is altered but it's still not visible in the results. + TrainJourney linked = link(tj1, tj2); + visit(linked, tj -> { System.out.print(tj.price + " - "); }); + System.out.println(); + + // ... but here, if this code is uncommented, tj2 will be appended + // at the end of the already altered tj1. This will cause a + // StackOverflowError from the endless visit() recursive calls on + // the tj2 part of the twice altered tj1. + /*TrainJourney linked2 = link(tj1, tj2); + visit(linked2, tj -> { System.out.print(tj.price + " - "); }); + System.out.println();*/ + } + + static class TrainJourney { + public int price; + public TrainJourney onward; + + public TrainJourney(int p, TrainJourney t) { + price = p; + onward = t; + } + } + + static TrainJourney link(TrainJourney a, TrainJourney b) { + if (a == null) { + return b; + } + TrainJourney t = a; + while (t.onward != null) { + t = t.onward; + } + t.onward = b; + return a; + } + + static TrainJourney append(TrainJourney a, TrainJourney b) { + return a == null ? b : new TrainJourney(a.price, append(a.onward, b)); + } + + static void visit(TrainJourney journey, Consumer c) { + if (journey != null) { + c.accept(journey); + visit(journey.onward, c); + } + } +} diff --git a/src/main/java/lambdasinaction/chap14/PersistentTree.java b/src/main/java/lambdasinaction/chap14/PersistentTree.java new file mode 100644 index 00000000..562d4f69 --- /dev/null +++ b/src/main/java/lambdasinaction/chap14/PersistentTree.java @@ -0,0 +1,82 @@ +package lambdasinaction.chap14; + +public class PersistentTree { + + public static void main(String[] args) { + Tree t = new Tree("Mary", 22, + new Tree("Emily", 20, + new Tree("Alan", 50, null, null), + new Tree("Georgie", 23, null, null) + ), + new Tree("Tian", 29, + new Tree("Raoul", 23, null, null), + null + ) + ); + + // found = 23 + System.out.println(lookup("Raoul", -1, t)); + // not found = -1 + System.out.println(lookup("Jeff", -1, t)); + + Tree f = fupdate("Jeff", 80, t); + // found = 80 + System.out.println(lookup("Jeff", -1, f)); + + Tree u = update("Jim", 40, t); + // t was not altered by fupdate, so Jeff is not found = -1 + System.out.println(lookup("Jeff", -1, u)); + // found = 40 + System.out.println(lookup("Jim", -1, u)); + + Tree f2 = fupdate("Jeff", 80, t); + // found = 80 + System.out.println(lookup("Jeff", -1, f2)); + // f2 built from t altered by update() above, so Jim is still present = 40 + System.out.println(lookup("Jim", -1, f2)); + } + + static class Tree { + private String key; + private int val; + private Tree left, right; + + public Tree(String k, int v, Tree l, Tree r) { + key = k; + val = v; + left = l; + right = r; + } + } + + public static int lookup(String k, int defaultval, Tree t) { + if (t == null) + return defaultval; + if (k.equals(t.key)) + return t.val; + return lookup(k, defaultval, k.compareTo(t.key) < 0 ? t.left : t.right); + } + + public static Tree update(String k, int newval, Tree t) { + if (t == null) + t = new Tree(k, newval, null, null); + else if (k.equals(t.key)) + t.val = newval; + else if (k.compareTo(t.key) < 0) + t.left = update(k, newval, t.left); + else + t.right = update(k, newval, t.right); + return t; + } + + public static Tree fupdate(String k, int newval, Tree t) { + return (t == null) ? + new Tree(k, newval, null, null) : + k.equals(t.key) ? + new Tree(k, newval, t.left, t.right) : + k.compareTo(t.key) < 0 ? + new Tree(t.key, t.val, fupdate(k,newval, t.left), t.right) : + new Tree(t.key, t.val, t.left, fupdate(k,newval, t.right)); + } + +} diff --git a/src/main/java/lambdasinaction/chap4/Reducing.java b/src/main/java/lambdasinaction/chap4/Reducing.java deleted file mode 100644 index 6bfc514d..00000000 --- a/src/main/java/lambdasinaction/chap4/Reducing.java +++ /dev/null @@ -1,29 +0,0 @@ -package lambdasinaction.chap4; -import java.util.stream.*; -import java.util.*; - -import static lambdasinaction.chap4.Dish.menu; - -public class Reducing{ - - public static void main(String...args){ - - List numbers = Arrays.asList(3,4,5,1,2); - int sum = numbers.stream().reduce(0, (a, b) -> a + b); - System.out.println(sum); - - int sum2 = numbers.stream().reduce(0, Integer::sum); - System.out.println(sum2); - - int max = numbers.stream().reduce(0, (a, b) -> Integer.max(a, b)); - System.out.println(max); - - Optional min = numbers.stream().reduce(Integer::min); - min.ifPresent(System.out::println); - - int calories = menu.stream() - .map(Dish::getCalories) - .reduce(0, Integer::sum); - System.out.println("Number of calories:" + calories); - } -} diff --git a/src/main/java/lambdasinaction/chap4/StreamBasic.java b/src/main/java/lambdasinaction/chap4/StreamBasic.java index 9beea005..19a8c176 100644 --- a/src/main/java/lambdasinaction/chap4/StreamBasic.java +++ b/src/main/java/lambdasinaction/chap4/StreamBasic.java @@ -24,7 +24,7 @@ public static void main(String...args){ public static List getLowCaloricDishesNamesInJava7(List dishes){ List lowCaloricDishes = new ArrayList<>(); for(Dish d: dishes){ - if(d.getCalories() > 400){ + if(d.getCalories() < 400){ lowCaloricDishes.add(d); } } @@ -42,7 +42,7 @@ public int compare(Dish d1, Dish d2){ public static List getLowCaloricDishesNamesInJava8(List dishes){ return dishes.stream() - .filter(d -> d.getCalories() > 400) + .filter(d -> d.getCalories() < 400) .sorted(comparing(Dish::getCalories)) .map(Dish::getName) .collect(toList()); diff --git a/src/main/java/lambdasinaction/chap4/StreamVsCollection.java b/src/main/java/lambdasinaction/chap4/StreamVsCollection.java index 18ed93ec..a72a0730 100644 --- a/src/main/java/lambdasinaction/chap4/StreamVsCollection.java +++ b/src/main/java/lambdasinaction/chap4/StreamVsCollection.java @@ -11,6 +11,8 @@ public static void main(String...args){ List names = Arrays.asList("Java8", "Lambdas", "In", "Action"); Stream s = names.stream(); s.forEach(System.out::println); - s.forEach(System.out::println); // Exception + // uncommenting this line will result in an IllegalStateException + // because streams can be consumed only once + //s.forEach(System.out::println); } } \ No newline at end of file diff --git a/src/main/java/lambdasinaction/chap4/BuildingStreams.java b/src/main/java/lambdasinaction/chap5/BuildingStreams.java similarity index 95% rename from src/main/java/lambdasinaction/chap4/BuildingStreams.java rename to src/main/java/lambdasinaction/chap5/BuildingStreams.java index ecb2dcb3..15280a39 100644 --- a/src/main/java/lambdasinaction/chap4/BuildingStreams.java +++ b/src/main/java/lambdasinaction/chap5/BuildingStreams.java @@ -1,9 +1,8 @@ -package lambdasinaction.chap4; +package lambdasinaction.chap5; import java.util.*; import java.util.function.IntSupplier; import java.util.stream.*; -import static java.util.stream.Collectors.toList; import java.nio.charset.Charset; import java.nio.file.*; @@ -67,7 +66,7 @@ public int getAsInt(){ }; IntStream.generate(fib).limit(10).forEach(System.out::println); - long uniqueWords = Files.lines(Paths.get("lambdasinaction/chap4/data.txt"), Charset.defaultCharset()) + long uniqueWords = Files.lines(Paths.get("lambdasinaction/chap5/data.txt"), Charset.defaultCharset()) .flatMap(line -> Arrays.stream(line.split(" "))) .distinct() .count(); diff --git a/src/main/java/lambdasinaction/chap5/Dish.java b/src/main/java/lambdasinaction/chap5/Dish.java deleted file mode 100644 index fab9f533..00000000 --- a/src/main/java/lambdasinaction/chap5/Dish.java +++ /dev/null @@ -1,52 +0,0 @@ -package lambdasinaction.chap5; - -import java.util.*; - -public class Dish { - - private final String name; - private final boolean vegetarian; - private final int calories; - private final Type type; - - public Dish(String name, boolean vegetarian, int calories, Type type) { - this.name = name; - this.vegetarian = vegetarian; - this.calories = calories; - this.type = type; - } - - public String getName() { - return name; - } - - public boolean isVegetarian() { - return vegetarian; - } - - public int getCalories() { - return calories; - } - - public Type getType() { - return type; - } - - public enum Type { MEAT, FISH, OTHER } - - @Override - public String toString() { - return name; - } - - public static final List menu = - Arrays.asList( new Dish("pork", false, 800, Dish.Type.MEAT), - new Dish("beef", false, 700, Dish.Type.MEAT), - new Dish("chicken", false, 400, Dish.Type.MEAT), - new Dish("french fries", true, 530, Dish.Type.OTHER), - new Dish("rice", true, 350, Dish.Type.OTHER), - new Dish("season fruit", true, 120, Dish.Type.OTHER), - new Dish("pizza", true, 550, Dish.Type.OTHER), - new Dish("prawns", false, 400, Dish.Type.FISH), - new Dish("salmon", false, 450, Dish.Type.FISH)); -} \ No newline at end of file diff --git a/src/main/java/lambdasinaction/chap4/Filtering.java b/src/main/java/lambdasinaction/chap5/Filtering.java similarity index 94% rename from src/main/java/lambdasinaction/chap4/Filtering.java rename to src/main/java/lambdasinaction/chap5/Filtering.java index adfc0d9c..066c8242 100644 --- a/src/main/java/lambdasinaction/chap4/Filtering.java +++ b/src/main/java/lambdasinaction/chap5/Filtering.java @@ -1,4 +1,6 @@ -package lambdasinaction.chap4; +package lambdasinaction.chap5; +import lambdasinaction.chap4.*; + import java.util.stream.*; import java.util.*; import static java.util.stream.Collectors.toList; diff --git a/src/main/java/lambdasinaction/chap4/Finding.java b/src/main/java/lambdasinaction/chap5/Finding.java similarity index 94% rename from src/main/java/lambdasinaction/chap4/Finding.java rename to src/main/java/lambdasinaction/chap5/Finding.java index 9eade942..acccc543 100644 --- a/src/main/java/lambdasinaction/chap4/Finding.java +++ b/src/main/java/lambdasinaction/chap5/Finding.java @@ -1,4 +1,6 @@ -package lambdasinaction.chap4; +package lambdasinaction.chap5; +import lambdasinaction.chap4.*; + import java.util.stream.*; import java.util.*; diff --git a/src/main/java/lambdasinaction/chap4/Laziness.java b/src/main/java/lambdasinaction/chap5/Laziness.java similarity index 96% rename from src/main/java/lambdasinaction/chap4/Laziness.java rename to src/main/java/lambdasinaction/chap5/Laziness.java index 60446895..a3df328a 100644 --- a/src/main/java/lambdasinaction/chap4/Laziness.java +++ b/src/main/java/lambdasinaction/chap5/Laziness.java @@ -1,4 +1,4 @@ -package lambdasinaction.chap4; +package lambdasinaction.chap5; import java.util.Arrays; import java.util.List; diff --git a/src/main/java/lambdasinaction/chap4/Mapping.java b/src/main/java/lambdasinaction/chap5/Mapping.java similarity index 85% rename from src/main/java/lambdasinaction/chap4/Mapping.java rename to src/main/java/lambdasinaction/chap5/Mapping.java index 7752d20d..7bf4caba 100644 --- a/src/main/java/lambdasinaction/chap4/Mapping.java +++ b/src/main/java/lambdasinaction/chap5/Mapping.java @@ -1,4 +1,6 @@ -package lambdasinaction.chap4; +package lambdasinaction.chap5; + +import lambdasinaction.chap4.*; import java.util.*; import static java.util.stream.Collectors.toList; @@ -32,8 +34,8 @@ public static void main(String...args){ List numbers2 = Arrays.asList(6,7,8); List pairs = numbers1.stream() - .flatMap(i -> numbers2.stream() - .map(j -> new int[]{i, j}) + .flatMap((Integer i) -> numbers2.stream() + .map((Integer j) -> new int[]{i, j}) ) .filter(pair -> (pair[0] + pair[1]) % 3 == 0) .collect(toList()); diff --git a/src/main/java/lambdasinaction/chap4/NumericStreams.java b/src/main/java/lambdasinaction/chap5/NumericStreams.java similarity index 96% rename from src/main/java/lambdasinaction/chap4/NumericStreams.java rename to src/main/java/lambdasinaction/chap5/NumericStreams.java index 7822bf98..e5f8cca8 100644 --- a/src/main/java/lambdasinaction/chap4/NumericStreams.java +++ b/src/main/java/lambdasinaction/chap5/NumericStreams.java @@ -1,4 +1,6 @@ -package lambdasinaction.chap4; +package lambdasinaction.chap5; +import lambdasinaction.chap4.*; + import java.util.stream.*; import java.util.*; diff --git a/src/main/java/lambdasinaction/chap4/PuttingIntoPractice.java b/src/main/java/lambdasinaction/chap5/PuttingIntoPractice.java similarity index 98% rename from src/main/java/lambdasinaction/chap4/PuttingIntoPractice.java rename to src/main/java/lambdasinaction/chap5/PuttingIntoPractice.java index ae47f9a5..d0b13ae5 100644 --- a/src/main/java/lambdasinaction/chap4/PuttingIntoPractice.java +++ b/src/main/java/lambdasinaction/chap5/PuttingIntoPractice.java @@ -1,7 +1,9 @@ -package lambdasinaction.chap4; +package lambdasinaction.chap5; + +import lambdasinaction.chap5.*; import java.util.*; -import java.util.stream.*; + import static java.util.Comparator.comparing; import static java.util.stream.Collectors.toList; diff --git a/src/main/java/lambdasinaction/chap5/Reducing.java b/src/main/java/lambdasinaction/chap5/Reducing.java index ae5d519e..93aed73e 100644 --- a/src/main/java/lambdasinaction/chap5/Reducing.java +++ b/src/main/java/lambdasinaction/chap5/Reducing.java @@ -1,34 +1,31 @@ package lambdasinaction.chap5; +import lambdasinaction.chap4.*; -import java.util.*; -import java.util.function.*; import java.util.stream.*; +import java.util.*; -import static java.util.stream.Collectors.*; -import static lambdasinaction.chap5.Dish.menu; +import static lambdasinaction.chap4.Dish.menu; -public class Reducing { +public class Reducing{ - public static void main(String ... args) { - System.out.println("Total calories in menu: " + calculateTotalCalories()); - System.out.println("Total calories in menu: " + calculateTotalCaloriesWithMethodReference()); - System.out.println("Total calories in menu: " + calculateTotalCaloriesWithoutCollectors()); - System.out.println("Total calories in menu: " + calculateTotalCaloriesUsingSum()); - } + public static void main(String...args){ - private static int calculateTotalCalories() { - return menu.stream().collect(reducing(0, Dish::getCalories, (Integer i, Integer j) -> i + j)); - } + List numbers = Arrays.asList(3,4,5,1,2); + int sum = numbers.stream().reduce(0, (a, b) -> a + b); + System.out.println(sum); - private static int calculateTotalCaloriesWithMethodReference() { - return menu.stream().collect(reducing(0, Dish::getCalories, Integer::sum)); - } + int sum2 = numbers.stream().reduce(0, Integer::sum); + System.out.println(sum2); - private static int calculateTotalCaloriesWithoutCollectors() { - return menu.stream().map(Dish::getCalories).reduce(Integer::sum).get(); - } + int max = numbers.stream().reduce(0, (a, b) -> Integer.max(a, b)); + System.out.println(max); + + Optional min = numbers.stream().reduce(Integer::min); + min.ifPresent(System.out::println); - private static int calculateTotalCaloriesUsingSum() { - return menu.stream().mapToInt(Dish::getCalories).sum(); + int calories = menu.stream() + .map(Dish::getCalories) + .reduce(0, Integer::sum); + System.out.println("Number of calories:" + calories); } -} \ No newline at end of file +} diff --git a/src/main/java/lambdasinaction/chap4/Trader.java b/src/main/java/lambdasinaction/chap5/Trader.java similarity index 92% rename from src/main/java/lambdasinaction/chap4/Trader.java rename to src/main/java/lambdasinaction/chap5/Trader.java index b8bb83c8..7487a8c6 100644 --- a/src/main/java/lambdasinaction/chap4/Trader.java +++ b/src/main/java/lambdasinaction/chap5/Trader.java @@ -1,4 +1,4 @@ -package lambdasinaction.chap4; +package lambdasinaction.chap5; public class Trader{ private String name; diff --git a/src/main/java/lambdasinaction/chap4/Transaction.java b/src/main/java/lambdasinaction/chap5/Transaction.java similarity index 94% rename from src/main/java/lambdasinaction/chap4/Transaction.java rename to src/main/java/lambdasinaction/chap5/Transaction.java index 04da232b..754e3839 100644 --- a/src/main/java/lambdasinaction/chap4/Transaction.java +++ b/src/main/java/lambdasinaction/chap5/Transaction.java @@ -1,4 +1,5 @@ -package lambdasinaction.chap4; +package lambdasinaction.chap5; + public class Transaction{ private Trader trader; diff --git a/src/main/java/lambdasinaction/chap5/CollectorHarness.java b/src/main/java/lambdasinaction/chap6/CollectorHarness.java similarity index 75% rename from src/main/java/lambdasinaction/chap5/CollectorHarness.java rename to src/main/java/lambdasinaction/chap6/CollectorHarness.java index 5b2fcb6c..8370be16 100644 --- a/src/main/java/lambdasinaction/chap5/CollectorHarness.java +++ b/src/main/java/lambdasinaction/chap6/CollectorHarness.java @@ -1,13 +1,11 @@ -package lambdasinaction.chap5; +package lambdasinaction.chap6; import java.util.function.*; -import static lambdasinaction.chap5.PartitionPrimeNumbers.*; - public class CollectorHarness { public static void main(String[] args) { - System.out.println("Partitioning done in: " + execute(PartitionPrimeNumbers::partitionPrimes) + " msecs"); + //System.out.println("Partitioning done in: " + execute(PartitionPrimeNumbers::partitionPrimes) + " msecs"); System.out.println("Partitioning done in: " + execute(PartitionPrimeNumbers::partitionPrimesWithCustomCollector) + " msecs" ); } @@ -18,6 +16,7 @@ private static long execute(Consumer primePartitioner) { primePartitioner.accept(1_000_000); long duration = (System.nanoTime() - start) / 1_000_000; if (duration < fastest) fastest = duration; + System.out.println("done in " + duration); } return fastest; } diff --git a/src/main/java/lambdasinaction/chap6/Dish.java b/src/main/java/lambdasinaction/chap6/Dish.java new file mode 100644 index 00000000..adba6e93 --- /dev/null +++ b/src/main/java/lambdasinaction/chap6/Dish.java @@ -0,0 +1,68 @@ +package lambdasinaction.chap6; + +import java.util.*; + +import static java.util.Arrays.asList; + +public class Dish { + + private final String name; + private final boolean vegetarian; + private final int calories; + private final Type type; + + public Dish(String name, boolean vegetarian, int calories, Type type) { + this.name = name; + this.vegetarian = vegetarian; + this.calories = calories; + this.type = type; + } + + public String getName() { + return name; + } + + public boolean isVegetarian() { + return vegetarian; + } + + public int getCalories() { + return calories; + } + + public Type getType() { + return type; + } + + public enum Type { MEAT, FISH, OTHER } + + @Override + public String toString() { + return name; + } + + public static final List menu = + asList( new Dish("pork", false, 800, Dish.Type.MEAT), + new Dish("beef", false, 700, Dish.Type.MEAT), + new Dish("chicken", false, 400, Dish.Type.MEAT), + new Dish("french fries", true, 530, Dish.Type.OTHER), + new Dish("rice", true, 350, Dish.Type.OTHER), + new Dish("season fruit", true, 120, Dish.Type.OTHER), + new Dish("pizza", true, 550, Dish.Type.OTHER), + new Dish("prawns", false, 400, Dish.Type.FISH), + new Dish("salmon", false, 450, Dish.Type.FISH)); + + public static final Map> dishTags = new HashMap<>(); + + static { + dishTags.put("pork", asList("greasy", "salty")); + dishTags.put("beef", asList("salty", "roasted")); + dishTags.put("chicken", asList("fried", "crisp")); + dishTags.put("french fries", asList("greasy", "fried")); + dishTags.put("rice", asList("light", "natural")); + dishTags.put("season fruit", asList("fresh", "natural")); + dishTags.put("pizza", asList("tasty", "salty")); + dishTags.put("prawns", asList("tasty", "roasted")); + dishTags.put("salmon", asList("delicious", "fresh")); + } +} \ No newline at end of file diff --git a/src/main/java/lambdasinaction/chap5/Grouping.java b/src/main/java/lambdasinaction/chap6/Grouping.java similarity index 72% rename from src/main/java/lambdasinaction/chap5/Grouping.java rename to src/main/java/lambdasinaction/chap6/Grouping.java index 5dc1b7ce..9105cc80 100644 --- a/src/main/java/lambdasinaction/chap5/Grouping.java +++ b/src/main/java/lambdasinaction/chap6/Grouping.java @@ -1,11 +1,10 @@ -package lambdasinaction.chap5; +package lambdasinaction.chap6; import java.util.*; -import java.util.function.*; -import java.util.stream.*; import static java.util.stream.Collectors.*; -import static lambdasinaction.chap5.Dish.menu; +import static lambdasinaction.chap6.Dish.dishTags; +import static lambdasinaction.chap6.Dish.menu; public class Grouping { @@ -13,6 +12,9 @@ enum CaloricLevel { DIET, NORMAL, FAT }; public static void main(String ... args) { System.out.println("Dishes grouped by type: " + groupDishesByType()); + System.out.println("Dish names grouped by type: " + groupDishNamesByType()); + System.out.println("Dish tags grouped by type: " + groupDishTagsByType()); + System.out.println("Caloric dishes grouped by type: " + groupCaloricDishesByType()); System.out.println("Dishes grouped by caloric level: " + groupDishesByCaloricLevel()); System.out.println("Dishes grouped by type and caloric level: " + groupDishedByTypeAndCaloricLevel()); System.out.println("Count dishes in groups: " + countDishesInGroups()); @@ -26,6 +28,19 @@ private static Map> groupDishesByType() { return menu.stream().collect(groupingBy(Dish::getType)); } + private static Map> groupDishNamesByType() { + return menu.stream().collect(groupingBy(Dish::getType, mapping(Dish::getName, toList()))); + } + + private static Map> groupDishTagsByType() { + return menu.stream().collect(groupingBy(Dish::getType, flatMapping(dish -> dishTags.get( dish.getName() ).stream(), toSet()))); + } + + private static Map> groupCaloricDishesByType() { +// return menu.stream().filter(dish -> dish.getCalories() > 500).collect(groupingBy(Dish::getType)); + return menu.stream().collect(groupingBy(Dish::getType, filtering(dish -> dish.getCalories() > 500, toList()))); + } + private static Map> groupDishesByCaloricLevel() { return menu.stream().collect( groupingBy(dish -> { @@ -38,7 +53,7 @@ private static Map> groupDishesByCaloricLevel() { private static Map>> groupDishedByTypeAndCaloricLevel() { return menu.stream().collect( groupingBy(Dish::getType, - groupingBy(dish -> { + groupingBy((Dish dish) -> { if (dish.getCalories() <= 400) return CaloricLevel.DIET; else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL; else return CaloricLevel.FAT; @@ -54,7 +69,7 @@ private static Map countDishesInGroups() { private static Map> mostCaloricDishesByType() { return menu.stream().collect( groupingBy(Dish::getType, - reducing((d1, d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2))); + reducing((Dish d1, Dish d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2))); } private static Map mostCaloricDishesByTypeWithoutOprionals() { diff --git a/src/main/java/lambdasinaction/chap5/GroupingTransactions.java b/src/main/java/lambdasinaction/chap6/GroupingTransactions.java similarity index 99% rename from src/main/java/lambdasinaction/chap5/GroupingTransactions.java rename to src/main/java/lambdasinaction/chap6/GroupingTransactions.java index db075913..ce358acb 100644 --- a/src/main/java/lambdasinaction/chap5/GroupingTransactions.java +++ b/src/main/java/lambdasinaction/chap6/GroupingTransactions.java @@ -1,4 +1,4 @@ -package lambdasinaction.chap5; +package lambdasinaction.chap6; import java.util.*; diff --git a/src/main/java/lambdasinaction/chap5/PartitionPrimeNumbers.java b/src/main/java/lambdasinaction/chap6/PartitionPrimeNumbers.java similarity index 89% rename from src/main/java/lambdasinaction/chap5/PartitionPrimeNumbers.java rename to src/main/java/lambdasinaction/chap6/PartitionPrimeNumbers.java index 9192bdfb..69d7c4ca 100644 --- a/src/main/java/lambdasinaction/chap5/PartitionPrimeNumbers.java +++ b/src/main/java/lambdasinaction/chap6/PartitionPrimeNumbers.java @@ -1,11 +1,10 @@ -package lambdasinaction.chap5; +package lambdasinaction.chap6; import java.util.*; import java.util.function.*; import java.util.stream.*; import static java.util.stream.Collectors.*; -import static lambdasinaction.chap5.Dish.menu; import static java.util.stream.Collector.Characteristics.*; public class PartitionPrimeNumbers { @@ -17,25 +16,26 @@ public static void main(String ... args) { } public static Map> partitionPrimes(int n) { - return Stream.iterate(2, i -> i + 1).limit(n-1) + return IntStream.rangeClosed(2, n).boxed() .collect(partitioningBy(candidate -> isPrime(candidate))); } public static boolean isPrime(int candidate) { - return Stream.iterate(2, i -> i + 1) + return IntStream.rangeClosed(2, candidate-1) .limit((long) Math.floor(Math.sqrt((double) candidate)) - 1) .noneMatch(i -> candidate % i == 0); } public static Map> partitionPrimesWithCustomCollector(int n) { - return Stream.iterate(2, i -> i + 1).limit(n-1).collect(new PrimeNumbersCollector()); + return IntStream.rangeClosed(2, n).boxed().collect(new PrimeNumbersCollector()); } public static boolean isPrime(List primes, Integer candidate) { double candidateRoot = Math.sqrt((double) candidate); - return takeWhile(primes, i -> i <= candidateRoot).stream().noneMatch(i -> candidate % i == 0); + //return takeWhile(primes, i -> i <= candidateRoot).stream().noneMatch(i -> candidate % i == 0); + return primes.stream().takeWhile(i -> i <= candidateRoot).noneMatch(i -> candidate % i == 0); } - +/* public static List takeWhile(List list, Predicate p) { int i = 0; for (A item : list) { @@ -46,7 +46,7 @@ public static List takeWhile(List list, Predicate p) { } return list; } - +*/ public static class PrimeNumbersCollector implements Collector>, Map>> { diff --git a/src/main/java/lambdasinaction/chap5/Partitioning.java b/src/main/java/lambdasinaction/chap6/Partitioning.java similarity index 81% rename from src/main/java/lambdasinaction/chap5/Partitioning.java rename to src/main/java/lambdasinaction/chap6/Partitioning.java index 9918d1f7..2ec8dc79 100644 --- a/src/main/java/lambdasinaction/chap5/Partitioning.java +++ b/src/main/java/lambdasinaction/chap6/Partitioning.java @@ -1,11 +1,10 @@ -package lambdasinaction.chap5; +package lambdasinaction.chap6; import java.util.*; -import java.util.function.*; -import java.util.stream.*; +import static java.util.Comparator.comparingInt; import static java.util.stream.Collectors.*; -import static lambdasinaction.chap5.Dish.menu; +import static lambdasinaction.chap6.Dish.menu; public class Partitioning { @@ -27,7 +26,7 @@ private static Object mostCaloricPartitionedByVegetarian() { return menu.stream().collect( partitioningBy(Dish::isVegetarian, collectingAndThen( - reducing((d1, d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2), + maxBy(comparingInt(Dish::getCalories)), Optional::get))); } } diff --git a/src/main/java/lambdasinaction/chap6/Reducing.java b/src/main/java/lambdasinaction/chap6/Reducing.java new file mode 100644 index 00000000..fb7cf971 --- /dev/null +++ b/src/main/java/lambdasinaction/chap6/Reducing.java @@ -0,0 +1,30 @@ +package lambdasinaction.chap6; + +import static java.util.stream.Collectors.*; +import static lambdasinaction.chap6.Dish.menu; + +public class Reducing { + + public static void main(String ... args) { + System.out.println("Total calories in menu: " + calculateTotalCalories()); + System.out.println("Total calories in menu: " + calculateTotalCaloriesWithMethodReference()); + System.out.println("Total calories in menu: " + calculateTotalCaloriesWithoutCollectors()); + System.out.println("Total calories in menu: " + calculateTotalCaloriesUsingSum()); + } + + private static int calculateTotalCalories() { + return menu.stream().collect(reducing(0, Dish::getCalories, (Integer i, Integer j) -> i + j)); + } + + private static int calculateTotalCaloriesWithMethodReference() { + return menu.stream().collect(reducing(0, Dish::getCalories, Integer::sum)); + } + + private static int calculateTotalCaloriesWithoutCollectors() { + return menu.stream().map(Dish::getCalories).reduce(Integer::sum).get(); + } + + private static int calculateTotalCaloriesUsingSum() { + return menu.stream().mapToInt(Dish::getCalories).sum(); + } +} \ No newline at end of file diff --git a/src/main/java/lambdasinaction/chap5/Summarizing.java b/src/main/java/lambdasinaction/chap6/Summarizing.java similarity index 95% rename from src/main/java/lambdasinaction/chap5/Summarizing.java rename to src/main/java/lambdasinaction/chap6/Summarizing.java index 5e99b6cc..06f9af6d 100644 --- a/src/main/java/lambdasinaction/chap5/Summarizing.java +++ b/src/main/java/lambdasinaction/chap6/Summarizing.java @@ -1,11 +1,10 @@ -package lambdasinaction.chap5; +package lambdasinaction.chap6; import java.util.*; import java.util.function.*; -import java.util.stream.*; import static java.util.stream.Collectors.*; -import static lambdasinaction.chap5.Dish.menu; +import static lambdasinaction.chap6.Dish.menu; public class Summarizing { diff --git a/src/main/java/lambdasinaction/chap5/ToListCollector.java b/src/main/java/lambdasinaction/chap6/ToListCollector.java similarity index 96% rename from src/main/java/lambdasinaction/chap5/ToListCollector.java rename to src/main/java/lambdasinaction/chap6/ToListCollector.java index 19edcd6c..0621d96a 100644 --- a/src/main/java/lambdasinaction/chap5/ToListCollector.java +++ b/src/main/java/lambdasinaction/chap6/ToListCollector.java @@ -1,4 +1,4 @@ -package lambdasinaction.chap5; +package lambdasinaction.chap6; import java.util.*; import java.util.function.*; diff --git a/src/main/java/lambdasinaction/chap7/Application.java b/src/main/java/lambdasinaction/chap7/Application.java deleted file mode 100644 index 30607f38..00000000 --- a/src/main/java/lambdasinaction/chap7/Application.java +++ /dev/null @@ -1,12 +0,0 @@ -package lambdasinaction.chap7; -import java.util.Arrays; - -public class Application{ - - public static void main(String...args){ - BasicList printingList = new PrintingList<>(); - Utils.process(printingList); - } - -} - diff --git a/src/main/java/lambdasinaction/chap7/Bag.java b/src/main/java/lambdasinaction/chap7/Bag.java deleted file mode 100644 index 9e938576..00000000 --- a/src/main/java/lambdasinaction/chap7/Bag.java +++ /dev/null @@ -1,12 +0,0 @@ -package lambdasinaction.chap7; -import java.util.Iterator; -public class Bag implements Foreachable, Removeable{ - - public Iterator iterator(){ - // TODO - return null; - } - - // ... -} - diff --git a/src/main/java/lambdasinaction/chap7/BasicList.java b/src/main/java/lambdasinaction/chap7/BasicList.java deleted file mode 100644 index 8b06376b..00000000 --- a/src/main/java/lambdasinaction/chap7/BasicList.java +++ /dev/null @@ -1,11 +0,0 @@ -package lambdasinaction.chap7; - -import java.util.function.Consumer; - -public interface BasicList{ - public void add(T t); - public int size(); - //TODO: uncomment, read the README for instructions - //public void forEach(Consumer action); -} - diff --git a/src/main/java/lambdasinaction/chap7/Box.java b/src/main/java/lambdasinaction/chap7/Box.java deleted file mode 100644 index 7717f456..00000000 --- a/src/main/java/lambdasinaction/chap7/Box.java +++ /dev/null @@ -1,13 +0,0 @@ -package lambdasinaction.chap7; -import java.util.Iterator; -public class Box implements Sized, Foreachable{ - - public int size(){ - return 0; - } - - public Iterator iterator(){ - // TODO - return null; - } -} diff --git a/src/main/java/lambdasinaction/chap7/Foreachable.java b/src/main/java/lambdasinaction/chap7/Foreachable.java deleted file mode 100644 index 6709a3fa..00000000 --- a/src/main/java/lambdasinaction/chap7/Foreachable.java +++ /dev/null @@ -1,10 +0,0 @@ -package lambdasinaction.chap7; -import java.util.function.Consumer; - -public interface Foreachable extends Iterable{ - public default void forEach(Consumer block){ - for(T e: this){ - block.accept(e); - } - } -} diff --git a/src/main/java/lambdasinaction/chap6/ForkJoinSumCalculator.java b/src/main/java/lambdasinaction/chap7/ForkJoinSumCalculator.java similarity index 91% rename from src/main/java/lambdasinaction/chap6/ForkJoinSumCalculator.java rename to src/main/java/lambdasinaction/chap7/ForkJoinSumCalculator.java index 90a6e781..8f74a90a 100644 --- a/src/main/java/lambdasinaction/chap6/ForkJoinSumCalculator.java +++ b/src/main/java/lambdasinaction/chap7/ForkJoinSumCalculator.java @@ -1,11 +1,10 @@ -package lambdasinaction.chap6; +package lambdasinaction.chap7; import java.util.concurrent.RecursiveTask; -import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinTask; import java.util.stream.LongStream; -import static lambdasinaction.chap6.ParallelStreamsHarness.FORK_JOIN_POOL; +import static lambdasinaction.chap7.ParallelStreamsHarness.FORK_JOIN_POOL; public class ForkJoinSumCalculator extends RecursiveTask { diff --git a/src/main/java/lambdasinaction/chap7/ParallelStreamBenchmark.java b/src/main/java/lambdasinaction/chap7/ParallelStreamBenchmark.java new file mode 100644 index 00000000..cdd16aca --- /dev/null +++ b/src/main/java/lambdasinaction/chap7/ParallelStreamBenchmark.java @@ -0,0 +1,62 @@ +package lambdasinaction.chap7; + +import java.util.concurrent.TimeUnit; +import java.util.stream.LongStream; +import java.util.stream.Stream; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Warmup; + +@State(Scope.Thread) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@Fork(value=2, jvmArgs={"-Xms4G", "-Xmx4G"}) +@Measurement(iterations=2) +@Warmup(iterations=3) +public class ParallelStreamBenchmark { + + private static final long N = 10_000_000L; + + @Benchmark + public long iterativeSum() { + long result = 0; + for (long i = 1L; i <= N; i++) { + result += i; + } + return result; + } + + @Benchmark + public long sequentialSum() { + return Stream.iterate( 1L, i -> i + 1 ).limit(N).reduce( 0L, Long::sum ); + } + + @Benchmark + public long parallelSum() { + return Stream.iterate(1L, i -> i + 1).limit(N).parallel().reduce( 0L, Long::sum); + } + + @Benchmark + public long rangedSum() { + return LongStream.rangeClosed( 1, N ).reduce( 0L, Long::sum ); + } + + @Benchmark + public long parallelRangedSum() { + return LongStream.rangeClosed(1, N).parallel().reduce( 0L, Long::sum); + } + + @TearDown(Level.Invocation) + public void tearDown() { + System.gc(); + } +} diff --git a/src/main/java/lambdasinaction/chap6/ParallelStreams.java b/src/main/java/lambdasinaction/chap7/ParallelStreams.java similarity index 94% rename from src/main/java/lambdasinaction/chap6/ParallelStreams.java rename to src/main/java/lambdasinaction/chap7/ParallelStreams.java index 6182c3fc..e8b2a520 100644 --- a/src/main/java/lambdasinaction/chap6/ParallelStreams.java +++ b/src/main/java/lambdasinaction/chap7/ParallelStreams.java @@ -1,4 +1,4 @@ -package lambdasinaction.chap6; +package lambdasinaction.chap7; import java.util.stream.*; @@ -6,7 +6,7 @@ public class ParallelStreams { public static long iterativeSum(long n) { long result = 0; - for (long i = 0; i < n; i++) { + for (long i = 0; i <= n; i++) { result += i; } return result; diff --git a/src/main/java/lambdasinaction/chap6/ParallelStreamsHarness.java b/src/main/java/lambdasinaction/chap7/ParallelStreamsHarness.java similarity index 94% rename from src/main/java/lambdasinaction/chap6/ParallelStreamsHarness.java rename to src/main/java/lambdasinaction/chap7/ParallelStreamsHarness.java index 858e74d3..7d53c86f 100644 --- a/src/main/java/lambdasinaction/chap6/ParallelStreamsHarness.java +++ b/src/main/java/lambdasinaction/chap7/ParallelStreamsHarness.java @@ -1,4 +1,4 @@ -package lambdasinaction.chap6; +package lambdasinaction.chap7; import java.util.concurrent.*; import java.util.function.*; @@ -8,7 +8,7 @@ public class ParallelStreamsHarness { public static final ForkJoinPool FORK_JOIN_POOL = new ForkJoinPool(); public static void main(String[] args) { - System.out.println("Iterative Sum done in: " + measurePerf(ParallelStreams::sequentialSum, 10_000_000L) + " msecs"); + System.out.println("Iterative Sum done in: " + measurePerf(ParallelStreams::iterativeSum, 10_000_000L) + " msecs"); System.out.println("Sequential Sum done in: " + measurePerf(ParallelStreams::sequentialSum, 10_000_000L) + " msecs"); System.out.println("Parallel forkJoinSum done in: " + measurePerf(ParallelStreams::parallelSum, 10_000_000L) + " msecs" ); System.out.println("Range forkJoinSum done in: " + measurePerf(ParallelStreams::rangedSum, 10_000_000L) + " msecs"); diff --git a/src/main/java/lambdasinaction/chap7/PrintingList.java b/src/main/java/lambdasinaction/chap7/PrintingList.java deleted file mode 100644 index 85020714..00000000 --- a/src/main/java/lambdasinaction/chap7/PrintingList.java +++ /dev/null @@ -1,19 +0,0 @@ -package lambdasinaction.chap7; - -import java.util.List; -import java.util.ArrayList; - -public class PrintingList implements BasicList { - private List internalList = new ArrayList<>(); - - public void add(T t){ - System.out.println("Adding an element"); - this.internalList.add(t); - } - - public int size(){ - System.out.println("Size of list"); - return internalList.size(); - } -} - diff --git a/src/main/java/lambdasinaction/chap7/README b/src/main/java/lambdasinaction/chap7/README deleted file mode 100644 index 74659576..00000000 --- a/src/main/java/lambdasinaction/chap7/README +++ /dev/null @@ -1,36 +0,0 @@ -To try out the scenario described in section 7.1 on Evolving APIs you need to do the following: - -compile individual files as follows from the directory src/main/java: - -javac lambdasinaction/chap7/BasicList.java -javac lambdasinaction/chap7/PrintingList.java -javac lambdasinaction/chap7/Utils.java -javac lambdasinaction/chap7/Application.java - -You can run the application and everything will work: - -java lambdasinaction/chap7/Application - -You can now modify the interface BasicList and add the method "forEach". -Compile and run, no problem: - -javac lambdasinaction/chap7/BasicList.java - -Now modify Utils to use the new forEach method available on all kinds of BasicList. -Just uncomment the appropriate the line in Utils, compile, run, and you'll have a surprise! - -Exception in thread "main" java.lang.AbstractMethodError: lambdasinaction.chap7.PrintingList.forEach(Ljava/util/function/Consumer;)V - at lambdasinaction.chap7.Utils.process(Utils.java:6) - at lambdasinaction.chap7.Application.main(Application.java:8) - - -Note also that recompiling the whole application will fail because PrintingList doesn't implement -the new method forEach: - -javac lambdasinaction/chap7/PrintingList.java -lambdasinaction/chap7/PrintingList.java:6: error: PrintingList is not abstract and does not override abstract method forEach(Consumer) in BasicList -public class PrintingList implements BasicList { - ^ - where T is a type-variable: - T extends Object declared in class PrintingList -1 error diff --git a/src/main/java/lambdasinaction/chap7/Removeable.java b/src/main/java/lambdasinaction/chap7/Removeable.java deleted file mode 100644 index 766cbfc1..00000000 --- a/src/main/java/lambdasinaction/chap7/Removeable.java +++ /dev/null @@ -1,17 +0,0 @@ -package lambdasinaction.chap7; -import java.util.Iterator; -import java.util.function.Predicate; - -public interface Removeable extends Iterable{ - public default boolean removeIf(Predicate filter){ - boolean removed = false; - Iterator each = iterator(); - while(each.hasNext()) { - if(filter.test(each.next())) { - each.remove(); - removed = true; - } - } - return removed; - } -} diff --git a/src/main/java/lambdasinaction/chap7/Sized.java b/src/main/java/lambdasinaction/chap7/Sized.java deleted file mode 100644 index 5372b91b..00000000 --- a/src/main/java/lambdasinaction/chap7/Sized.java +++ /dev/null @@ -1,9 +0,0 @@ -package lambdasinaction.chap7; - -public interface Sized{ - public int size(); - public default boolean isEmpty() { - return size() == 0; - } -} - diff --git a/src/main/java/lambdasinaction/chap7/Utils.java b/src/main/java/lambdasinaction/chap7/Utils.java deleted file mode 100644 index 0ffa3d83..00000000 --- a/src/main/java/lambdasinaction/chap7/Utils.java +++ /dev/null @@ -1,11 +0,0 @@ -package lambdasinaction.chap7; - -public class Utils{ - public static void process(BasicList list){ - int s = list.size(); - //TODO: uncomment, read the README for instructions - //list.forEach(System.out::println); - } - - -} diff --git a/src/main/java/lambdasinaction/chap6/WordCount.java b/src/main/java/lambdasinaction/chap7/WordCount.java similarity index 99% rename from src/main/java/lambdasinaction/chap6/WordCount.java rename to src/main/java/lambdasinaction/chap7/WordCount.java index 762e0355..13ccce52 100644 --- a/src/main/java/lambdasinaction/chap6/WordCount.java +++ b/src/main/java/lambdasinaction/chap7/WordCount.java @@ -1,4 +1,4 @@ -package lambdasinaction.chap6; +package lambdasinaction.chap7; import java.util.*; import java.util.function.*; diff --git a/src/main/java/lambdasinaction/chap8/ChainOfResponsibilityMain.java b/src/main/java/lambdasinaction/chap8/ChainOfResponsibilityMain.java new file mode 100644 index 00000000..91d52e0a --- /dev/null +++ b/src/main/java/lambdasinaction/chap8/ChainOfResponsibilityMain.java @@ -0,0 +1,59 @@ +package lambdasinaction.chap8; + +import java.util.function.Function; +import java.util.function.UnaryOperator; + + +public class ChainOfResponsibilityMain { + + public static void main(String[] args) { + ProcessingObject p1 = new HeaderTextProcessing(); + ProcessingObject p2 = new SpellCheckerProcessing(); + p1.setSuccessor(p2); + String result1 = p1.handle("Aren't labdas really sexy?!!"); + System.out.println(result1); + + + UnaryOperator headerProcessing = + (String text) -> "From Raoul, Mario and Alan: " + text; + UnaryOperator spellCheckerProcessing = + (String text) -> text.replaceAll("labda", "lambda"); + Function pipeline = headerProcessing.andThen(spellCheckerProcessing); + String result2 = pipeline.apply("Aren't labdas really sexy?!!"); + System.out.println(result2); + } + + static private abstract class ProcessingObject { + protected ProcessingObject successor; + + public void setSuccessor(ProcessingObject successor) { + this.successor = successor; + } + + public T handle(T input) { + T r = handleWork(input); + if (successor != null) { + return successor.handle(r); + } + return r; + } + + abstract protected T handleWork(T input); + } + + static private class HeaderTextProcessing + extends ProcessingObject { + public String handleWork(String text) { + return "From Raoul, Mario and Alan: " + text; + } + } + + static private class SpellCheckerProcessing + extends ProcessingObject { + public String handleWork(String text) { + return text.replaceAll("labda", "lambda"); + } + } +} + + diff --git a/src/main/java/lambdasinaction/chap8/Debugging.java b/src/main/java/lambdasinaction/chap8/Debugging.java new file mode 100644 index 00000000..40c4dd23 --- /dev/null +++ b/src/main/java/lambdasinaction/chap8/Debugging.java @@ -0,0 +1,30 @@ +package lambdasinaction.chap8; + + +import java.util.*; + +public class Debugging{ + public static void main(String[] args) { + List points = Arrays.asList(new Point(12, 2), null); + points.stream().map(p -> p.getX()).forEach(System.out::println); + } + + + private static class Point{ + private int x; + private int y; + + private Point(int x, int y) { + this.x = x; + this.y = y; + } + + public int getX() { + return x; + } + + public void setX(int x) { + this.x = x; + } + } +} diff --git a/src/main/java/lambdasinaction/chap8/FactoryMain.java b/src/main/java/lambdasinaction/chap8/FactoryMain.java new file mode 100644 index 00000000..e30d219e --- /dev/null +++ b/src/main/java/lambdasinaction/chap8/FactoryMain.java @@ -0,0 +1,48 @@ +package lambdasinaction.chap8; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; + + +public class FactoryMain { + + public static void main(String[] args) { + Product p1 = ProductFactory.createProduct("loan"); + + Supplier loanSupplier = Loan::new; + Product p2 = loanSupplier.get(); + + Product p3 = ProductFactory.createProductLambda("loan"); + + } + + static private class ProductFactory { + public static Product createProduct(String name){ + switch(name){ + case "loan": return new Loan(); + case "stock": return new Stock(); + case "bond": return new Bond(); + default: throw new RuntimeException("No such product " + name); + } + } + + public static Product createProductLambda(String name){ + Supplier p = map.get(name); + if(p != null) return p.get(); + throw new RuntimeException("No such product " + name); + } + } + + static private interface Product {} + static private class Loan implements Product {} + static private class Stock implements Product {} + static private class Bond implements Product {} + + final static private Map> map = new HashMap<>(); + static { + map.put("loan", Loan::new); + map.put("stock", Stock::new); + map.put("bond", Bond::new); + } +} diff --git a/src/main/java/lambdasinaction/chap8/ObserverMain.java b/src/main/java/lambdasinaction/chap8/ObserverMain.java new file mode 100644 index 00000000..dcd8cb50 --- /dev/null +++ b/src/main/java/lambdasinaction/chap8/ObserverMain.java @@ -0,0 +1,79 @@ +package lambdasinaction.chap8; + +import java.util.ArrayList; +import java.util.List; + + +public class ObserverMain { + + public static void main(String[] args) { + Feed f = new Feed(); + f.registerObserver(new NYTimes()); + f.registerObserver(new Guardian()); + f.registerObserver(new LeMonde()); + f.notifyObservers("The queen said her favourite book is Java 8 in Action!"); + + + Feed feedLambda = new Feed(); + + feedLambda.registerObserver((String tweet) -> { + if(tweet != null && tweet.contains("money")){ + System.out.println("Breaking news in NY! " + tweet); } + }); + feedLambda.registerObserver((String tweet) -> { + if(tweet != null && tweet.contains("queen")){ + System.out.println("Yet another news in London... " + tweet); } + }); + + feedLambda.notifyObservers("Money money money, give me money!"); + + } + + + interface Observer{ + void inform(String tweet); + } + + interface Subject{ + void registerObserver(Observer o); + void notifyObservers(String tweet); + } + + static private class NYTimes implements Observer{ + @Override + public void inform(String tweet) { + if(tweet != null && tweet.contains("money")){ + System.out.println("Breaking news in NY!" + tweet); + } + } + } + + static private class Guardian implements Observer{ + @Override + public void inform(String tweet) { + if(tweet != null && tweet.contains("queen")){ + System.out.println("Yet another news in London... " + tweet); + } + } + } + + static private class LeMonde implements Observer{ + @Override + public void inform(String tweet) { + if(tweet != null && tweet.contains("wine")){ + System.out.println("Today cheese, wine and news! " + tweet); + } + } + } + + static private class Feed implements Subject{ + private final List observers = new ArrayList<>(); + public void registerObserver(Observer o) { + this.observers.add(o); + } + public void notifyObservers(String tweet) { + observers.forEach(o -> o.inform(tweet)); + } + } + +} diff --git a/src/main/java/lambdasinaction/chap8/OnlineBanking.java b/src/main/java/lambdasinaction/chap8/OnlineBanking.java new file mode 100644 index 00000000..27cbf917 --- /dev/null +++ b/src/main/java/lambdasinaction/chap8/OnlineBanking.java @@ -0,0 +1,18 @@ +package lambdasinaction.chap8; + + +abstract class OnlineBanking { + public void processCustomer(int id){ + Customer c = Database.getCustomerWithId(id); + makeCustomerHappy(c); + } + abstract void makeCustomerHappy(Customer c); + + + // dummy Customer class + static private class Customer {} + // dummy Datbase class + static private class Database{ + static Customer getCustomerWithId(int id){ return new Customer();} + } +} diff --git a/src/main/java/lambdasinaction/chap8/OnlineBankingLambda.java b/src/main/java/lambdasinaction/chap8/OnlineBankingLambda.java new file mode 100644 index 00000000..3bd9e049 --- /dev/null +++ b/src/main/java/lambdasinaction/chap8/OnlineBankingLambda.java @@ -0,0 +1,23 @@ +package lambdasinaction.chap8; + +import java.util.function.Consumer; + + +public class OnlineBankingLambda { + + public static void main(String[] args) { + new OnlineBankingLambda().processCustomer(1337, (Customer c) -> System.out.println("Hello!")); + } + + public void processCustomer(int id, Consumer makeCustomerHappy){ + Customer c = Database.getCustomerWithId(id); + makeCustomerHappy.accept(c); + } + + // dummy Customer class + static private class Customer {} + // dummy Database class + static private class Database{ + static Customer getCustomerWithId(int id){ return new Customer();} + } +} diff --git a/src/main/java/lambdasinaction/chap8/Peek.java b/src/main/java/lambdasinaction/chap8/Peek.java new file mode 100644 index 00000000..e25d68a4 --- /dev/null +++ b/src/main/java/lambdasinaction/chap8/Peek.java @@ -0,0 +1,19 @@ +package lambdasinaction.chap8; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.stream.Collectors.toList; + + +public class Peek { + + public static void main(String[] args) { + + List result = Stream.of(2, 3, 4, 5) + .peek(x -> System.out.println("taking from stream: " + x)).map(x -> x + 17) + .peek(x -> System.out.println("after map: " + x)).filter(x -> x % 2 == 0) + .peek(x -> System.out.println("after filter: " + x)).limit(3) + .peek(x -> System.out.println("after limit: " + x)).collect(toList()); + } +} diff --git a/src/main/java/lambdasinaction/chap8/StrategyMain.java b/src/main/java/lambdasinaction/chap8/StrategyMain.java new file mode 100644 index 00000000..cc899aa8 --- /dev/null +++ b/src/main/java/lambdasinaction/chap8/StrategyMain.java @@ -0,0 +1,44 @@ +package lambdasinaction.chap8; + + +public class StrategyMain { + + public static void main(String[] args) { + // old school + Validator v1 = new Validator(new IsNumeric()); + System.out.println(v1.validate("aaaa")); + Validator v2 = new Validator(new IsAllLowerCase ()); + System.out.println(v2.validate("bbbb")); + + + // with lambdas + Validator v3 = new Validator((String s) -> s.matches("\\d+")); + System.out.println(v3.validate("aaaa")); + Validator v4 = new Validator((String s) -> s.matches("[a-z]+")); + System.out.println(v4.validate("bbbb")); + } + + interface ValidationStrategy { + public boolean execute(String s); + } + + static private class IsAllLowerCase implements ValidationStrategy { + public boolean execute(String s){ + return s.matches("[a-z]+"); + } + } + static private class IsNumeric implements ValidationStrategy { + public boolean execute(String s){ + return s.matches("\\d+"); + } + } + + static private class Validator{ + private final ValidationStrategy strategy; + public Validator(ValidationStrategy v){ + this.strategy = v; + } + public boolean validate(String s){ + return strategy.execute(s); } + } +} diff --git a/src/main/java/lambdasinaction/chap7/Ambiguous.java b/src/main/java/lambdasinaction/chap9/Ambiguous.java similarity index 78% rename from src/main/java/lambdasinaction/chap7/Ambiguous.java rename to src/main/java/lambdasinaction/chap9/Ambiguous.java index 80563837..d007bedc 100644 --- a/src/main/java/lambdasinaction/chap7/Ambiguous.java +++ b/src/main/java/lambdasinaction/chap9/Ambiguous.java @@ -1,4 +1,4 @@ -package lambdasinaction.chap7; +package lambdasinaction.chap9; public class Ambiguous{ @@ -7,13 +7,13 @@ public static void main(String... args) { } static interface A{ - public default void hello() { + public default void hello() { System.out.println("Hello from A"); } } static interface B { - public default void hello() { + public default void hello() { System.out.println("Hello from B"); } } diff --git a/src/main/java/lambdasinaction/chap7/Diamond.java b/src/main/java/lambdasinaction/chap9/Diamond.java similarity index 77% rename from src/main/java/lambdasinaction/chap7/Diamond.java rename to src/main/java/lambdasinaction/chap9/Diamond.java index ec82bbed..f0226e1d 100644 --- a/src/main/java/lambdasinaction/chap7/Diamond.java +++ b/src/main/java/lambdasinaction/chap9/Diamond.java @@ -1,7 +1,7 @@ -package lambdasinaction.chap7; +package lambdasinaction.chap9; public class Diamond{ - + public static void main(String...args){ new D().hello(); } @@ -11,13 +11,13 @@ public default void hello(){ System.out.println("Hello from A"); } } - + static interface B extends A { } - + static interface C extends A { } - static class D implements B, C { - + static class D implements B, C { + } } diff --git a/src/main/java/lambdasinaction/chap9/Drawable.java b/src/main/java/lambdasinaction/chap9/Drawable.java new file mode 100644 index 00000000..731a8772 --- /dev/null +++ b/src/main/java/lambdasinaction/chap9/Drawable.java @@ -0,0 +1,8 @@ +package lambdasinaction.chap9; + +/** + * Created by raoul-gabrielurma on 15/01/2014. + */ +public interface Drawable{ + public void draw(); +} diff --git a/src/main/java/lambdasinaction/chap9/Ellipse.java b/src/main/java/lambdasinaction/chap9/Ellipse.java new file mode 100644 index 00000000..8f2d1ae0 --- /dev/null +++ b/src/main/java/lambdasinaction/chap9/Ellipse.java @@ -0,0 +1,36 @@ +package lambdasinaction.chap9; + +/** + * Created by raoul-gabrielurma on 15/01/2014. + */ +public class Ellipse implements Resizable { + @Override + public int getWidth() { + return 0; + } + + @Override + public int getHeight() { + return 0; + } + + @Override + public void setWidth(int width) { + + } + + @Override + public void setHeight(int height) { + + } + + @Override + public void setAbsoluteSize(int width, int height) { + + } + + @Override + public void draw() { + + } +} diff --git a/src/main/java/lambdasinaction/chap9/Game.java b/src/main/java/lambdasinaction/chap9/Game.java new file mode 100644 index 00000000..0836f46c --- /dev/null +++ b/src/main/java/lambdasinaction/chap9/Game.java @@ -0,0 +1,16 @@ +package lambdasinaction.chap9; + + +import java.util.Arrays; +import java.util.List; + +public class Game { + + public static void main(String...args){ + List resizableShapes = + Arrays.asList(new Square(), + new Triangle(), new Ellipse()); + Utils.paint(resizableShapes); + } +} + diff --git a/src/main/java/lambdasinaction/chap7/Intro.java b/src/main/java/lambdasinaction/chap9/Intro.java similarity index 73% rename from src/main/java/lambdasinaction/chap7/Intro.java rename to src/main/java/lambdasinaction/chap9/Intro.java index 885edc2b..d540a705 100644 --- a/src/main/java/lambdasinaction/chap7/Intro.java +++ b/src/main/java/lambdasinaction/chap9/Intro.java @@ -1,6 +1,8 @@ -package lambdasinaction.chap7; +package lambdasinaction.chap9; -import java.util.*; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; public class Intro{ diff --git a/src/main/java/lambdasinaction/chap7/Letter.java b/src/main/java/lambdasinaction/chap9/Letter.java similarity index 88% rename from src/main/java/lambdasinaction/chap7/Letter.java rename to src/main/java/lambdasinaction/chap9/Letter.java index 39a9d3df..2ff65193 100644 --- a/src/main/java/lambdasinaction/chap7/Letter.java +++ b/src/main/java/lambdasinaction/chap9/Letter.java @@ -1,4 +1,4 @@ -package lambdasinaction.chap7; +package lambdasinaction.chap9; import java.util.function.Function; @@ -12,7 +12,7 @@ public static String addFooter(String text){ } public static String checkSpelling(String text){ - return text.replaceAll("C\\+\\+", "**Censored**"); + return text.replaceAll("C\\+\\+", "**Censored**"); } diff --git a/src/main/java/lambdasinaction/chap7/MostSpecific.java b/src/main/java/lambdasinaction/chap9/MostSpecific.java similarity index 79% rename from src/main/java/lambdasinaction/chap7/MostSpecific.java rename to src/main/java/lambdasinaction/chap9/MostSpecific.java index 9ab1cd8c..2a845e72 100644 --- a/src/main/java/lambdasinaction/chap7/MostSpecific.java +++ b/src/main/java/lambdasinaction/chap9/MostSpecific.java @@ -1,4 +1,4 @@ -package lambdasinaction.chap7; +package lambdasinaction.chap9; public class MostSpecific{ @@ -9,13 +9,13 @@ public static void main(String... args) { } static interface A{ - public default void hello() { + public default void hello() { System.out.println("Hello from A"); } } static interface B extends A{ - public default void hello() { + public default void hello() { System.out.println("Hello from B"); } } @@ -23,16 +23,16 @@ public default void hello() { static class C implements B, A {} static class D implements A{} - + static class E extends D implements B, A{} - + static class F implements B, A { - public void hello() { + public void hello() { System.out.println("Hello from F"); } } static class G extends F implements B, A{} - + } diff --git a/src/main/java/lambdasinaction/chap9/OptionalMain.java b/src/main/java/lambdasinaction/chap9/OptionalMain.java deleted file mode 100644 index 1db2ab03..00000000 --- a/src/main/java/lambdasinaction/chap9/OptionalMain.java +++ /dev/null @@ -1,13 +0,0 @@ -package lambdasinaction.chap9; - -import java.util.*; - -public class OptionalMain { - - public String getCarInsuranceName(Optional person) { - return person.flatMap(Person::getCar) - .flatMap(Car::getInsurance) - .map(Insurance::getName) - .orElse("Unknown"); - } -} diff --git a/src/main/java/lambdasinaction/chap9/README b/src/main/java/lambdasinaction/chap9/README new file mode 100644 index 00000000..bdbebd1f --- /dev/null +++ b/src/main/java/lambdasinaction/chap9/README @@ -0,0 +1,37 @@ +To try out the scenario described in section 8.1 on Evolving APIs you need to do the following: + +compile individual files as follows from the directory src/main/java: + +javac lambdasinaction/chap8/Resizable.java +javac lambdasinaction/chap8/Ellipse.java +javac lambdasinaction/chap8/Utils.java +javac lambdasinaction/chap8/Game.java + +You can run the application and everything will work: + +java lambdasinaction/chap8/Game + +You can now modify the interface Resizable and add the method "setRelativeSize". +Compile and run, no problem: + +javac lambdasinaction/chap8/Resizable.java + +Now modify Utils to use the new setRelativeSize method available on all kinds of Resizable. +Just uncomment the appropriate the line in Utils, compile, run, and you'll have a surprise! + +Exception in thread "main" java.lang.AbstractMethodError: lambdasinaction.chap8.Square.setRelativeSize(II)V + +Note also that recompiling the whole application will fail because Ellipse doesn't implement +the new method setRelativeSize: + +javac lambdasinaction/chap8/Ellipse.java +lambdasinaction/chap7/Ellipse.java:6: error: Ellipse is not abstract and does not override abstract method setRelativeSize(int,int) in Resizable +public class Ellipse implements Resizable { + ^ +1 error + +The problem can be fixed by ensuring that setRelativeSize is a default method: + +public default void setRelativeSize(int widthFactor, int heightFactor){ + // a default implementation +} diff --git a/src/main/java/lambdasinaction/chap9/Resizable.java b/src/main/java/lambdasinaction/chap9/Resizable.java new file mode 100644 index 00000000..c197b2bb --- /dev/null +++ b/src/main/java/lambdasinaction/chap9/Resizable.java @@ -0,0 +1,12 @@ +package lambdasinaction.chap9; + +public interface Resizable extends Drawable{ + public int getWidth(); + public int getHeight(); + public void setWidth(int width); + public void setHeight(int height); + public void setAbsoluteSize(int width, int height); + //TODO: uncomment, read the README for instructions + //public void setRelativeSize(int widthFactor, int heightFactor); +} + diff --git a/src/main/java/lambdasinaction/chap9/Square.java b/src/main/java/lambdasinaction/chap9/Square.java new file mode 100644 index 00000000..11c2397f --- /dev/null +++ b/src/main/java/lambdasinaction/chap9/Square.java @@ -0,0 +1,36 @@ +package lambdasinaction.chap9; + +/** + * Created by raoul-gabrielurma on 15/01/2014. + */ +public class Square implements Resizable { + @Override + public int getWidth() { + return 0; + } + + @Override + public int getHeight() { + return 0; + } + + @Override + public void setWidth(int width) { + + } + + @Override + public void setHeight(int height) { + + } + + @Override + public void setAbsoluteSize(int width, int height) { + + } + + @Override + public void draw() { + + } +} diff --git a/src/main/java/lambdasinaction/chap9/Triangle.java b/src/main/java/lambdasinaction/chap9/Triangle.java new file mode 100644 index 00000000..ab98fa72 --- /dev/null +++ b/src/main/java/lambdasinaction/chap9/Triangle.java @@ -0,0 +1,36 @@ +package lambdasinaction.chap9; + +/** + * Created by raoul-gabrielurma on 15/01/2014. + */ +public class Triangle implements Resizable { + @Override + public int getWidth() { + return 0; + } + + @Override + public int getHeight() { + return 0; + } + + @Override + public void setWidth(int width) { + + } + + @Override + public void setHeight(int height) { + + } + + @Override + public void setAbsoluteSize(int width, int height) { + + } + + @Override + public void draw() { + + } +} diff --git a/src/main/java/lambdasinaction/chap9/Utils.java b/src/main/java/lambdasinaction/chap9/Utils.java new file mode 100644 index 00000000..9e2af4bd --- /dev/null +++ b/src/main/java/lambdasinaction/chap9/Utils.java @@ -0,0 +1,13 @@ +package lambdasinaction.chap9; + +import java.util.List; + +public class Utils{ + public static void paint(List l){ + l.forEach(r -> { r.setAbsoluteSize(42, 42); }); + + //TODO: uncomment, read the README for instructions + //l.forEach(r -> { r.setRelativeSize(2, 2); }); + } + +} diff --git a/src/main/java/lambdasinaction/dsl/Grouping.java b/src/main/java/lambdasinaction/dsl/Grouping.java new file mode 100644 index 00000000..b1472cf0 --- /dev/null +++ b/src/main/java/lambdasinaction/dsl/Grouping.java @@ -0,0 +1,79 @@ +/* + * Copyright 2005 JBoss Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package lambdasinaction.dsl; + +import lambdasinaction.chap6.Dish; + +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collector; + +import static java.util.stream.Collectors.groupingBy; +import static lambdasinaction.chap6.Dish.menu; +import static lambdasinaction.dsl.Grouping.GroupingBuilder.groupOn; + +public class Grouping { + + enum CaloricLevel { DIET, NORMAL, FAT }; + + public static void main(String ... args) { + System.out.println("Dishes grouped by type and caloric level: " + groupDishedByTypeAndCaloricLevel2()); + System.out.println("Dishes grouped by type and caloric level: " + groupDishedByTypeAndCaloricLevel3()); + } + + private static CaloricLevel getCaloricLevel( Dish dish ) { + if (dish.getCalories() <= 400) return CaloricLevel.DIET; + else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL; + else return CaloricLevel.FAT; + } + + private static Map>> groupDishedByTypeAndCaloricLevel2() { + return menu.stream().collect( + twoLevelGroupingBy(Dish::getType, dish -> getCaloricLevel( dish ) ) + ); + } + + public static Collector>>> twoLevelGroupingBy(Function f1, Function f2) { + return groupingBy(f1, groupingBy(f2)); + } + + private static Map>> groupDishedByTypeAndCaloricLevel3() { + Collector>>> c = groupOn( ( Dish dish ) -> getCaloricLevel( dish ) ).after( Dish::getType ).get(); + return menu.stream().collect( c ); + } + + public static class GroupingBuilder { + private final Collector> collector; + + public GroupingBuilder( Collector> collector ) { + this.collector = collector; + } + + public Collector> get() { + return collector; + } + + public GroupingBuilder, J> after(Function classifier) { + return new GroupingBuilder<>( groupingBy( classifier, collector ) ); + } + + public static GroupingBuilder, K> groupOn(Function classifier) { + return new GroupingBuilder<>( groupingBy( classifier ) ); + } + } +} diff --git a/src/main/java/lambdasinaction/dsl/LambdaOrderBuilder.java b/src/main/java/lambdasinaction/dsl/LambdaOrderBuilder.java new file mode 100644 index 00000000..6746c3b1 --- /dev/null +++ b/src/main/java/lambdasinaction/dsl/LambdaOrderBuilder.java @@ -0,0 +1,83 @@ +/* + * Copyright 2005 JBoss Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package lambdasinaction.dsl; + +import lambdasinaction.dsl.model.Order; +import lambdasinaction.dsl.model.Stock; +import lambdasinaction.dsl.model.Trade; + +import java.util.function.Consumer; + +public class LambdaOrderBuilder { + + private Order order = new Order(); + + public static Order order(Consumer consumer) { + LambdaOrderBuilder builder = new LambdaOrderBuilder(); + consumer.accept( builder ); + return builder.order; + } + + public void forCustomer(String customer) { + order.setCustomer( customer ); + } + + public void buy(Consumer consumer) { + trade( consumer, Trade.Type.BUY ); + } + + public void sell(Consumer consumer) { + trade( consumer, Trade.Type.SELL ); + } + + private void trade( Consumer consumer, Trade.Type type ) { + TradeBuilder builder = new TradeBuilder(); + builder.trade.setType( type ); + consumer.accept( builder ); + order.addTrade( builder.trade ); + } + + public static class TradeBuilder { + private Trade trade = new Trade(); + + public void quantity(int quantity) { + trade.setQuantity( quantity ); + } + + public void price(double price) { + trade.setPrice( price ); + } + + public void stock(Consumer consumer) { + StockBuilder builder = new StockBuilder(); + consumer.accept( builder ); + trade.setStock( builder.stock ); + } + } + + public static class StockBuilder { + private Stock stock = new Stock(); + + public void symbol(String symbol) { + stock.setSymbol( symbol ); + } + + public void market(String market) { + stock.setMarket( market ); + } + } +} diff --git a/src/main/java/lambdasinaction/dsl/Main.java b/src/main/java/lambdasinaction/dsl/Main.java new file mode 100644 index 00000000..b50683e5 --- /dev/null +++ b/src/main/java/lambdasinaction/dsl/Main.java @@ -0,0 +1,98 @@ +/* + * Copyright 2005 JBoss Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package lambdasinaction.dsl; + +import lambdasinaction.dsl.model.Order; +import lambdasinaction.dsl.model.Stock; +import lambdasinaction.dsl.model.Trade; + +import static lambdasinaction.dsl.MethodChainingOrderBuilder.forCustomer; +import static lambdasinaction.dsl.NestedFunctionOrderBuilder.*; + +public class Main { + + public void plain() { + Order order = new Order(); + order.setCustomer( "BigBank" ); + + Trade trade1 = new Trade(); + trade1.setType( Trade.Type.BUY ); + + Stock stock1 = new Stock(); + stock1.setSymbol( "IBM" ); + stock1.setMarket( "NYSE" ); + + trade1.setStock( stock1 ); + trade1.setPrice( 125.00 ); + trade1.setQuantity( 80 ); + order.addTrade( trade1 ); + + Trade trade2 = new Trade(); + trade2.setType( Trade.Type.BUY ); + + Stock stock2 = new Stock(); + stock2.setSymbol( "GOOGLE" ); + stock2.setMarket( "NASDAQ" ); + + trade2.setStock( stock2 ); + trade2.setPrice( 375.00 ); + trade2.setQuantity( 50 ); + order.addTrade( trade2 ); + } + + public void methodChaining() { + Order order = forCustomer( "BigBank" ) + .buy( 80 ).stock( "IBM" ).on( "NYSE" ).at( 125.00 ) + .sell( 50 ).stock( "GOOGLE" ).on( "NASDAQ" ).at( 375.00 ) + .end(); + + } + + public void nestedFunction() { + Order order = order("BigBank", + buy(80, + stock( "IBM", on( "NYSE" ) ), + at(125.00)), + sell(50, + stock("GOOGLE", on("NASDAQ")), + at(375.00)) + ); + } + + public void lambda() { + Order order = LambdaOrderBuilder.order( o -> { + o.forCustomer( "BigBank" ); + o.buy( t -> { + t.quantity( 80 ); + t.price( 125.00 ); + t.stock( s -> { + s.symbol( "IBM" ); + s.market( "NYSE" ); + } ); + }); + o.sell( t -> { + t.quantity( 50 ); + t.price( 375.00 ); + t.stock( s -> { + s.symbol( "GOOGLE" ); + s.market( "NASDAQ" ); + } ); + }); + } ); + } + +} diff --git a/src/main/java/lambdasinaction/dsl/MethodChainingOrderBuilder.java b/src/main/java/lambdasinaction/dsl/MethodChainingOrderBuilder.java new file mode 100644 index 00000000..7d771ec5 --- /dev/null +++ b/src/main/java/lambdasinaction/dsl/MethodChainingOrderBuilder.java @@ -0,0 +1,99 @@ +/* + * Copyright 2005 JBoss Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package lambdasinaction.dsl; + +import lambdasinaction.dsl.model.Order; +import lambdasinaction.dsl.model.Stock; +import lambdasinaction.dsl.model.Trade; + +public class MethodChainingOrderBuilder { + + public final Order order = new Order(); + + private MethodChainingOrderBuilder(String customer) { + order.setCustomer( customer ); + } + + public static MethodChainingOrderBuilder forCustomer( String customer ) { + return new MethodChainingOrderBuilder(customer); + } + + public Order end() { + return order; + } + + public TradeBuilder buy(int quantity) { + return new TradeBuilder( this, Trade.Type.BUY, quantity ); + } + + public TradeBuilder sell(int quantity) { + return new TradeBuilder( this, Trade.Type.SELL, quantity ); + } + + private MethodChainingOrderBuilder addTrade(Trade trade) { + order.addTrade( trade ); + return this; + } + + public static class TradeBuilder { + private final MethodChainingOrderBuilder builder; + public final Trade trade = new Trade(); + + private TradeBuilder(MethodChainingOrderBuilder builder, Trade.Type type, int quantity) { + this.builder = builder; + trade.setType( type ); + trade.setQuantity( quantity ); + } + + public StockBuilder stock(String symbol) { + return new StockBuilder( builder, trade, symbol ); + } + } + + public static class TradeBuilderWithStock { + private final MethodChainingOrderBuilder builder; + private final Trade trade; + + public TradeBuilderWithStock( MethodChainingOrderBuilder builder, Trade trade ) { + this.builder = builder; + this.trade = trade; + } + + public MethodChainingOrderBuilder at(double price) { + trade.setPrice( price ); + return builder.addTrade( trade ); + } + } + + public static class StockBuilder { + private final MethodChainingOrderBuilder builder; + private final Trade trade; + private final Stock stock = new Stock(); + + private StockBuilder(MethodChainingOrderBuilder builder, Trade trade, String symbol) { + this.builder = builder; + this.trade = trade; + stock.setSymbol( symbol ); + } + + public TradeBuilderWithStock on(String market) { + stock.setMarket( market ); + trade.setStock( stock ); + return new TradeBuilderWithStock( builder, trade ); + } + } +} diff --git a/src/main/java/lambdasinaction/dsl/Mixed.java b/src/main/java/lambdasinaction/dsl/Mixed.java new file mode 100644 index 00000000..132dd998 --- /dev/null +++ b/src/main/java/lambdasinaction/dsl/Mixed.java @@ -0,0 +1,39 @@ +/* + * Copyright 2005 JBoss Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package lambdasinaction.dsl; + +import lambdasinaction.dsl.model.Order; + +import static lambdasinaction.dsl.MixedBuilder.buy; +import static lambdasinaction.dsl.MixedBuilder.sell; +import static lambdasinaction.dsl.MixedBuilder.forCustomer; + +public class Mixed { + public void mixed() { + Order order = + forCustomer( "BigBank", + buy( t -> t.quantity( 80 ) + .stock( "IBM" ) + .on( "NYSE" ) + .at( 125.00 )), + sell( t -> t.quantity( 50 ) + .stock( "GOOGLE" ) + .on( "NASDAQ" ) + .at( 125.00 )) ); + + } +} diff --git a/src/main/java/lambdasinaction/dsl/MixedBuilder.java b/src/main/java/lambdasinaction/dsl/MixedBuilder.java new file mode 100644 index 00000000..98d7ea97 --- /dev/null +++ b/src/main/java/lambdasinaction/dsl/MixedBuilder.java @@ -0,0 +1,85 @@ +/* + * Copyright 2005 JBoss Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package lambdasinaction.dsl; + +import lambdasinaction.dsl.model.Order; +import lambdasinaction.dsl.model.Stock; +import lambdasinaction.dsl.model.Trade; + +import java.util.function.Consumer; +import java.util.stream.Stream; + +public class MixedBuilder { + + public static Order forCustomer(String customer, TradeBuilder... builders) { + Order order = new Order(); + order.setCustomer( customer ); + Stream.of(builders).forEach( b -> order.addTrade( b.trade ) ); + return order; + } + + public static TradeBuilder buy(Consumer consumer) { + return buildTrade( consumer, Trade.Type.BUY ); + } + + public static TradeBuilder sell(Consumer consumer) { + return buildTrade( consumer, Trade.Type.SELL ); + } + + private static TradeBuilder buildTrade( Consumer consumer, Trade.Type buy ) { + TradeBuilder builder = new TradeBuilder(); + builder.trade.setType( buy ); + consumer.accept( builder ); + return builder; + } + + public static class TradeBuilder { + private Trade trade = new Trade(); + + public TradeBuilder quantity(int quantity) { + trade.setQuantity( quantity ); + return this; + } + + public TradeBuilder at(double price) { + trade.setPrice( price ); + return this; + } + + public StockBuilder stock(String symbol) { + return new StockBuilder(this, trade, symbol); + } + } + + public static class StockBuilder { + private final TradeBuilder builder; + private final Trade trade; + private final Stock stock = new Stock(); + + private StockBuilder(TradeBuilder builder, Trade trade, String symbol) { + this.builder = builder; + this.trade = trade; + stock.setSymbol( symbol ); + } + + public TradeBuilder on(String market) { + stock.setMarket( market ); + trade.setStock( stock ); + return builder; + } + } +} diff --git a/src/main/java/lambdasinaction/dsl/NestedFunctionOrderBuilder.java b/src/main/java/lambdasinaction/dsl/NestedFunctionOrderBuilder.java new file mode 100644 index 00000000..b9daaac9 --- /dev/null +++ b/src/main/java/lambdasinaction/dsl/NestedFunctionOrderBuilder.java @@ -0,0 +1,64 @@ +/* + * Copyright 2005 JBoss Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package lambdasinaction.dsl; + +import lambdasinaction.dsl.model.Order; +import lambdasinaction.dsl.model.Stock; +import lambdasinaction.dsl.model.Trade; + +import java.util.stream.Stream; + +public class NestedFunctionOrderBuilder { + + public static Order order(String customer, Trade... trades) { + Order order = new Order(); + order.setCustomer( customer ); + Stream.of(trades).forEach( order::addTrade ); + return order; + } + + public static Trade buy(int quantity, Stock stock, double price) { + return buildTrade( stock, price, Trade.Type.BUY ); + } + + public static Trade sell(int quantity, Stock stock, double price) { + return buildTrade( stock, price, Trade.Type.SELL ); + } + + private static Trade buildTrade( Stock stock, double price, Trade.Type buy ) { + Trade trade = new Trade(); + trade.setType( buy ); + trade.setStock( stock ); + trade.setPrice( price ); + return trade; + } + + public static double at(double price) { + return price; + } + + public static Stock stock(String symbol, String market) { + Stock stock = new Stock(); + stock.setSymbol( symbol ); + stock.setMarket( market ); + return stock; + } + + public static String on(String market) { + return market; + } +} diff --git a/src/main/java/lambdasinaction/dsl/TaxCalculator.java b/src/main/java/lambdasinaction/dsl/TaxCalculator.java new file mode 100644 index 00000000..9ef38cef --- /dev/null +++ b/src/main/java/lambdasinaction/dsl/TaxCalculator.java @@ -0,0 +1,81 @@ +/* + * Copyright 2005 JBoss Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package lambdasinaction.dsl; + +import lambdasinaction.dsl.model.Order; +import lambdasinaction.dsl.model.Tax; + +import java.util.function.Function; + +public class TaxCalculator { + + public static double calculate( Order order, boolean useRegional, boolean useGeneral, boolean useSurcharge ) { + double value = order.getValue(); + if (useRegional) value = Tax.regional(value); + if (useGeneral) value = Tax.general(value); + if (useSurcharge) value = Tax.surcharge(value); + return value; + } + + private boolean useRegional; + private boolean useGeneral; + private boolean useSurcharge; + + public TaxCalculator withTaxRegional() { + useRegional = true; + return this; + } + + public TaxCalculator withTaxGeneral() { + useGeneral= true; + return this; + } + + public TaxCalculator withTaxSurcharge() { + useSurcharge = true; + return this; + } + + public double calculate(Order order) { + return calculate( order, useRegional, useGeneral, useSurcharge ); + } + + public Function taxFuncion = Function.identity(); + + public TaxCalculator with(Function f) { + taxFuncion.andThen( f ); + return this; + } + + public double calculateF(Order order) { + return taxFuncion.apply( order.getValue() ); + } + + public static void main(String[] args) { + Order order = new Order(); + + double value = TaxCalculator.calculate( order, true, false, true ); + + value = new TaxCalculator().withTaxRegional() + .withTaxSurcharge() + .calculate( order ); + + value = new TaxCalculator().with(Tax::regional) + .with(Tax::surcharge) + .calculate( order ); + } +} diff --git a/src/main/java/lambdasinaction/dsl/model/Order.java b/src/main/java/lambdasinaction/dsl/model/Order.java new file mode 100644 index 00000000..21c06747 --- /dev/null +++ b/src/main/java/lambdasinaction/dsl/model/Order.java @@ -0,0 +1,43 @@ +/* + * Copyright 2005 JBoss Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package lambdasinaction.dsl.model; + +import java.util.ArrayList; +import java.util.List; + +public class Order { + + private String customer; + + private List trades = new ArrayList<>(); + + public void addTrade( Trade trade ) { + trades.add( trade ); + } + + public String getCustomer() { + return customer; + } + + public void setCustomer( String customer ) { + this.customer = customer; + } + + public double getValue() { + return trades.stream().mapToDouble( Trade::getValue ).sum(); + } +} \ No newline at end of file diff --git a/src/main/java/lambdasinaction/dsl/model/Stock.java b/src/main/java/lambdasinaction/dsl/model/Stock.java new file mode 100644 index 00000000..1e0302e2 --- /dev/null +++ b/src/main/java/lambdasinaction/dsl/model/Stock.java @@ -0,0 +1,40 @@ +/* + * Copyright 2005 JBoss Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package lambdasinaction.dsl.model; + +public class Stock { + + private String symbol; + + private String market; + + public String getSymbol() { + return symbol; + } + + public void setSymbol( String symbol ) { + this.symbol = symbol; + } + + public String getMarket() { + return market; + } + + public void setMarket( String market ) { + this.market = market; + } +} diff --git a/src/main/java/lambdasinaction/dsl/model/Tax.java b/src/main/java/lambdasinaction/dsl/model/Tax.java new file mode 100644 index 00000000..d2d6bf22 --- /dev/null +++ b/src/main/java/lambdasinaction/dsl/model/Tax.java @@ -0,0 +1,31 @@ +/* + * Copyright 2005 JBoss Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package lambdasinaction.dsl.model; + +public class Tax { + public static double regional(double value) { + return value * 1.1; + } + + public static double general(double value) { + return value * 1.3; + } + + public static double surcharge(double value) { + return value * 1.05; + } +} diff --git a/src/main/java/lambdasinaction/dsl/model/Trade.java b/src/main/java/lambdasinaction/dsl/model/Trade.java new file mode 100644 index 00000000..c8f35813 --- /dev/null +++ b/src/main/java/lambdasinaction/dsl/model/Trade.java @@ -0,0 +1,66 @@ +/* + * Copyright 2005 JBoss Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package lambdasinaction.dsl.model; + +public class Trade { + + public enum Type { BUY, SELL } + + private Type type; + + private Stock stock; + + private int quantity; + + private double price; + + public Type getType() { + return type; + } + + public void setType( Type type ) { + this.type = type; + } + + public int getQuantity() { + return quantity; + } + + public void setQuantity( int quantity ) { + this.quantity = quantity; + } + + public double getPrice() { + return price; + } + + public void setPrice( double price ) { + this.price = price; + } + + public Stock getStock() { + return stock; + } + + public void setStock( Stock stock ) { + this.stock = stock; + } + + public double getValue() { + return quantity * price; + } +} diff --git a/src/main/resources/lambdasinaction/chap4/data.txt b/src/main/resources/lambdasinaction/chap5/data.txt similarity index 100% rename from src/main/resources/lambdasinaction/chap4/data.txt rename to src/main/resources/lambdasinaction/chap5/data.txt