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 97% rename from src/main/java/lambdasinaction/chap6/StreamForkerExample.java rename to src/main/java/lambdasinaction/appc/StreamForkerExample.java index 551f816c..fe2dacd5 100644 --- a/src/main/java/lambdasinaction/chap6/StreamForkerExample.java +++ b/src/main/java/lambdasinaction/appc/StreamForkerExample.java @@ -1,4 +1,4 @@ -package lambdasinaction.chap6; +package lambdasinaction.appc; import lambdasinaction.chap5.*; 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/chap10/AsyncShop.java b/src/main/java/lambdasinaction/chap10/AsyncShop.java index 0813e63d..4d230e7d 100644 --- a/src/main/java/lambdasinaction/chap10/AsyncShop.java +++ b/src/main/java/lambdasinaction/chap10/AsyncShop.java @@ -1,10 +1,11 @@ package lambdasinaction.chap10; -import java.text.*; -import java.util.*; -import java.util.concurrent.*; +import static lambdasinaction.chap10.Util.delay; +import static lambdasinaction.chap10.Util.format; -import static lambdasinaction.chap10.Util.*; +import java.util.Random; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; public class AsyncShop { diff --git a/src/main/java/lambdasinaction/chap10/AsyncShopClient.java b/src/main/java/lambdasinaction/chap10/AsyncShopClient.java index 297c24d5..029e9b30 100644 --- a/src/main/java/lambdasinaction/chap10/AsyncShopClient.java +++ b/src/main/java/lambdasinaction/chap10/AsyncShopClient.java @@ -1,6 +1,6 @@ package lambdasinaction.chap10; -import java.util.concurrent.*; +import java.util.concurrent.Future; public class AsyncShopClient { diff --git a/src/main/java/lambdasinaction/chap10/BestPriceFinder.java b/src/main/java/lambdasinaction/chap10/BestPriceFinder.java index 15a75671..25cf3870 100644 --- a/src/main/java/lambdasinaction/chap10/BestPriceFinder.java +++ b/src/main/java/lambdasinaction/chap10/BestPriceFinder.java @@ -1,8 +1,13 @@ package lambdasinaction.chap10; -import java.util.*; -import java.util.concurrent.*; -import java.util.stream.*; +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 { @@ -20,34 +25,8 @@ public Thread newThread(Runnable r) { return t; } }); -/* - public List findPriceSequential(String product) { - return shops.stream() - .map(shop -> shop.getName() + " price is " + shop.calculatePrice(product)) - .collect(Collectors.toList()); - } - - public List findPriceParallel(String product) { - return shops.parallelStream() - .map(shop -> shop.getName() + " price is " + shop.calculatePrice(product)) - .collect(Collectors.toList()); - } - - public List findPrice(String product) { - List> priceFutures = - shops.stream() - .map(shop -> CompletableFuture.supplyAsync(() -> shop.getName() + " price is " - + shop.calculatePrice(product), executor)) - .collect(Collectors.toList()); - List prices = priceFutures.stream() - .map(CompletableFuture::join) - .collect(Collectors.toList()); - return prices; - //return sequence(priceFutures).join(); - } -/*/ - public List findPriceSequential(String product) { + public List findPricesSequential(String product) { return shops.stream() .map(shop -> shop.getPrice(product)) .map(Quote::parse) @@ -55,7 +34,7 @@ public List findPriceSequential(String product) { .collect(Collectors.toList()); } - public List findPriceParallel(String product) { + public List findPricesParallel(String product) { return shops.parallelStream() .map(shop -> shop.getPrice(product)) .map(Quote::parse) @@ -63,8 +42,8 @@ public List findPriceParallel(String product) { .collect(Collectors.toList()); } - public List findPrice(String product) { - List> priceFutures = findPriceStream(product) + public List findPricesFuture(String product) { + List> priceFutures = findPricesStream(product) .collect(Collectors.>toList()); return priceFutures.stream() @@ -72,16 +51,16 @@ public List findPrice(String product) { .collect(Collectors.toList()); } - public Stream> findPriceStream(String product) { + 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() { + public void printPricesStream(String product) { long start = System.nanoTime(); - CompletableFuture[] futures = findPriceStream("myPhone") + 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(); diff --git a/src/main/java/lambdasinaction/chap10/BestPriceFinderMain.java b/src/main/java/lambdasinaction/chap10/BestPriceFinderMain.java index 19d70ac7..1f4f5612 100644 --- a/src/main/java/lambdasinaction/chap10/BestPriceFinderMain.java +++ b/src/main/java/lambdasinaction/chap10/BestPriceFinderMain.java @@ -1,20 +1,17 @@ package lambdasinaction.chap10; -import java.util.*; -import java.util.concurrent.*; -import java.util.function.*; - -import static java.util.stream.Collectors.toList; +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.findPriceSequential("myPhone")); - //execute("parallel", () -> bestPriceFinder.findPriceParallel("myPhone")); - execute("composed CompletableFuture", () -> bestPriceFinder.findPrice("myPhone")); - //bestPriceFinder.printPricesStream(); + 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) { @@ -23,4 +20,5 @@ private static void execute(String msg, Supplier> s) { long duration = (System.nanoTime() - start) / 1_000_000; System.out.println(msg + " done in " + duration + " msecs"); } + } diff --git a/src/main/java/lambdasinaction/chap10/Discount.java b/src/main/java/lambdasinaction/chap10/Discount.java index 994ad182..e0c0b072 100644 --- a/src/main/java/lambdasinaction/chap10/Discount.java +++ b/src/main/java/lambdasinaction/chap10/Discount.java @@ -1,8 +1,7 @@ package lambdasinaction.chap10; -import java.util.*; - -import static lambdasinaction.chap10.Util.*; +import static lambdasinaction.chap10.Util.delay; +import static lambdasinaction.chap10.Util.format; public class Discount { diff --git a/src/main/java/lambdasinaction/chap10/ExchangeService.java b/src/main/java/lambdasinaction/chap10/ExchangeService.java new file mode 100644 index 00000000..9ae1ccdb --- /dev/null +++ b/src/main/java/lambdasinaction/chap10/ExchangeService.java @@ -0,0 +1,26 @@ +package lambdasinaction.chap10; + +import static lambdasinaction.chap10.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/chap10/Shop.java b/src/main/java/lambdasinaction/chap10/Shop.java index e1ee1fc3..4aa88ec7 100644 --- a/src/main/java/lambdasinaction/chap10/Shop.java +++ b/src/main/java/lambdasinaction/chap10/Shop.java @@ -1,8 +1,9 @@ package lambdasinaction.chap10; -import java.util.*; +import static lambdasinaction.chap10.Util.delay; +import static lambdasinaction.chap10.Util.format; -import static lambdasinaction.chap10.Util.*; +import java.util.Random; public class Shop { diff --git a/src/main/java/lambdasinaction/chap10/Util.java b/src/main/java/lambdasinaction/chap10/Util.java index 8b4c00fc..f0a0f5f4 100644 --- a/src/main/java/lambdasinaction/chap10/Util.java +++ b/src/main/java/lambdasinaction/chap10/Util.java @@ -1,9 +1,12 @@ package lambdasinaction.chap10; -import java.text.*; -import java.util.*; -import java.util.concurrent.*; -import java.util.stream.*; +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 { diff --git a/src/main/java/lambdasinaction/chap10/v1/BestPriceFinder.java b/src/main/java/lambdasinaction/chap10/v1/BestPriceFinder.java new file mode 100644 index 00000000..42022883 --- /dev/null +++ b/src/main/java/lambdasinaction/chap10/v1/BestPriceFinder.java @@ -0,0 +1,163 @@ +package lambdasinaction.chap10.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.chap10.ExchangeService; +import lambdasinaction.chap10.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/chap10/v1/BestPriceFinderMain.java b/src/main/java/lambdasinaction/chap10/v1/BestPriceFinderMain.java new file mode 100644 index 00000000..c1c0f80a --- /dev/null +++ b/src/main/java/lambdasinaction/chap10/v1/BestPriceFinderMain.java @@ -0,0 +1,25 @@ +package lambdasinaction.chap10.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/chap10/v1/Shop.java b/src/main/java/lambdasinaction/chap10/v1/Shop.java new file mode 100644 index 00000000..9a4350d3 --- /dev/null +++ b/src/main/java/lambdasinaction/chap10/v1/Shop.java @@ -0,0 +1,41 @@ +package lambdasinaction.chap10.v1; + +import static lambdasinaction.chap10.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/chap10/v1/ShopMain.java b/src/main/java/lambdasinaction/chap10/v1/ShopMain.java new file mode 100644 index 00000000..b972713f --- /dev/null +++ b/src/main/java/lambdasinaction/chap10/v1/ShopMain.java @@ -0,0 +1,32 @@ +package lambdasinaction.chap10.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/chap11/DateTimeExamples.java b/src/main/java/lambdasinaction/chap11/DateTimeExamples.java index df7b2732..ff11ee61 100644 --- a/src/main/java/lambdasinaction/chap11/DateTimeExamples.java +++ b/src/main/java/lambdasinaction/chap11/DateTimeExamples.java @@ -1,15 +1,27 @@ package lambdasinaction.chap11; -import java.text.*; -import java.time.*; -import java.time.chrono.*; -import java.time.format.*; -import java.time.temporal.*; -import java.util.*; - -import static java.time.temporal.ChronoField.DAY_OF_WEEK; -import static java.time.temporal.ChronoUnit.DAYS; -import static java.time.temporal.TemporalAdjusters.*; +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 { @@ -20,9 +32,9 @@ protected DateFormat initialValue() { }; public static void main(String[] args) { - //useOldDate(); + useOldDate(); useLocalDate(); - //useTemporalAdjuster(); + useTemporalAdjuster(); useDateFormatter(); } @@ -33,7 +45,7 @@ private static void useOldDate() { System.out.println(formatters.get().format(date)); Calendar calendar = Calendar.getInstance(); - calendar.set(2014, 2, 18); + calendar.set(2014, Calendar.FEBRUARY, 18); System.out.println(calendar); } @@ -101,11 +113,11 @@ private static void useTemporalAdjuster() { date = date.with(nextOrSame(DayOfWeek.FRIDAY)); System.out.println(date); date = date.with(temporal -> { - int dow = temporal.get(DAY_OF_WEEK); + DayOfWeek dow = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK)); int dayToAdd = 1; - if (dow == 5) dayToAdd = 3; - if (dow == 6) dayToAdd = 2; - return temporal.plus(dayToAdd, DAYS); + if (dow == DayOfWeek.FRIDAY) dayToAdd = 3; + if (dow == DayOfWeek.SATURDAY) dayToAdd = 2; + return temporal.plus(dayToAdd, ChronoUnit.DAYS); }); System.out.println(date); } @@ -113,10 +125,10 @@ private static void useTemporalAdjuster() { private static class NextWorkingDay implements TemporalAdjuster { @Override public Temporal adjustInto(Temporal temporal) { - int dow = temporal.get(ChronoField.DAY_OF_WEEK); + DayOfWeek dow = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK)); int dayToAdd = 1; - if (dow == 5) dayToAdd = 3; - if (dow == 6) dayToAdd = 2; + if (dow == DayOfWeek.FRIDAY) dayToAdd = 3; + if (dow == DayOfWeek.SATURDAY) dayToAdd = 2; return temporal.plus(dayToAdd, ChronoUnit.DAYS); } } diff --git a/src/main/java/lambdasinaction/chap12/SubsetsMain.java b/src/main/java/lambdasinaction/chap12/SubsetsMain.java index 7fa3fc89..61c52fb5 100644 --- a/src/main/java/lambdasinaction/chap12/SubsetsMain.java +++ b/src/main/java/lambdasinaction/chap12/SubsetsMain.java @@ -5,17 +5,13 @@ 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<>(); @@ -30,9 +26,11 @@ public static List> subsets(List l) { } public static List> insertAll(Integer first, List> lists) { - List> result = new ArrayList<>(); for (List l : lists) { - List copyList = new ArrayList<>(l); + List> result = new ArrayList<>(); + for (List l : lists) { + List copyList = new ArrayList<>(); copyList.add(first); + copyList.addAll(l); result.add(copyList); } return result; diff --git a/src/main/java/lambdasinaction/chap13/Combinators.java b/src/main/java/lambdasinaction/chap13/Combinators.java index f9e3b1a7..4fb99db0 100644 --- a/src/main/java/lambdasinaction/chap13/Combinators.java +++ b/src/main/java/lambdasinaction/chap13/Combinators.java @@ -1,19 +1,18 @@ package lambdasinaction.chap13; - 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)); + System.out.println(repeat(3, (Integer x) -> 2 * x).apply(10)); } - static Function compose(Function g, Function f) { + 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, (Function) repeat(n-1, f)); + 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/chap13/Currying.java b/src/main/java/lambdasinaction/chap13/Currying.java index 4f366f42..d1fc3551 100644 --- a/src/main/java/lambdasinaction/chap13/Currying.java +++ b/src/main/java/lambdasinaction/chap13/Currying.java @@ -13,6 +13,9 @@ public static void main(String[] args) { 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) { @@ -23,4 +26,7 @@ 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/chap13/LazyLists.java b/src/main/java/lambdasinaction/chap13/LazyLists.java index 72fac731..43d73173 100644 --- a/src/main/java/lambdasinaction/chap13/LazyLists.java +++ b/src/main/java/lambdasinaction/chap13/LazyLists.java @@ -3,13 +3,11 @@ 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())); + MyList l = new MyLinkedList<>(5, new MyLinkedList<>(10, + new Empty())); System.out.println(l.head()); @@ -19,69 +17,79 @@ public static void main(String[] args) { 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))); + // 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); + 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){ + + public MyList filter(Predicate p) { return this; } } - - static class LazyList implements MyList{ + static class LazyList implements MyList { final T head; final Supplier> tail; + public LazyList(T head, Supplier> tail) { this.head = head; - this.tail = tail; + this.tail = tail; } + public T head() { return head; } @@ -89,31 +97,29 @@ public T 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); + 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)); + 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))); + return new LazyList<>(numbers.head(), () -> primes(numbers.tail() + .filter(n -> n % numbers.head() != 0))); } - static void printAll(MyList numbers){ - if (numbers.isEmpty()){ + static void printAll(MyList numbers) { + if (numbers.isEmpty()) { return; } System.out.println(numbers.head()); @@ -121,5 +127,3 @@ static void printAll(MyList numbers){ } } - - diff --git a/src/main/java/lambdasinaction/chap13/PatternMatching.java b/src/main/java/lambdasinaction/chap13/PatternMatching.java index 3de10b0c..cf9af017 100644 --- a/src/main/java/lambdasinaction/chap13/PatternMatching.java +++ b/src/main/java/lambdasinaction/chap13/PatternMatching.java @@ -1,36 +1,137 @@ package lambdasinaction.chap13; - 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 Expr { } - static class Number extends Expr { int val; } - static class BinOp extends Expr { String opname; Expr left, right; } + 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{ + 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{ + 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/chap13/PersistentDataStructures.java b/src/main/java/lambdasinaction/chap13/PersistentDataStructures.java deleted file mode 100644 index a2650b14..00000000 --- a/src/main/java/lambdasinaction/chap13/PersistentDataStructures.java +++ /dev/null @@ -1,32 +0,0 @@ -package lambdasinaction.chap13; - - -public class PersistentDataStructures { - - - 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)); - } -} diff --git a/src/main/java/lambdasinaction/chap13/PersistentTrainJourney.java b/src/main/java/lambdasinaction/chap13/PersistentTrainJourney.java new file mode 100644 index 00000000..8569f57f --- /dev/null +++ b/src/main/java/lambdasinaction/chap13/PersistentTrainJourney.java @@ -0,0 +1,66 @@ +package lambdasinaction.chap13; + +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/chap13/PersistentTree.java b/src/main/java/lambdasinaction/chap13/PersistentTree.java new file mode 100644 index 00000000..e6b88b70 --- /dev/null +++ b/src/main/java/lambdasinaction/chap13/PersistentTree.java @@ -0,0 +1,82 @@ +package lambdasinaction.chap13; + +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/Mapping.java b/src/main/java/lambdasinaction/chap4/Mapping.java index 7752d20d..9adf3c99 100644 --- a/src/main/java/lambdasinaction/chap4/Mapping.java +++ b/src/main/java/lambdasinaction/chap4/Mapping.java @@ -32,8 +32,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/chap5/Grouping.java b/src/main/java/lambdasinaction/chap5/Grouping.java index 5dc1b7ce..9065d02f 100644 --- a/src/main/java/lambdasinaction/chap5/Grouping.java +++ b/src/main/java/lambdasinaction/chap5/Grouping.java @@ -38,7 +38,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 +54,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() {