├── .gitignore ├── LICENSE ├── README.md ├── pom.xml └── src └── main ├── java └── lambdasinaction │ ├── appa │ ├── Author.java │ ├── Authors.java │ └── Book.java │ ├── appc │ ├── StreamForker.java │ └── StreamForkerExample.java │ ├── appd │ ├── InnerClass.java │ └── Lambda.java │ ├── chap1 │ └── FilteringApples.java │ ├── chap10 │ ├── Car.java │ ├── Insurance.java │ ├── OperationsWithOptional.java │ ├── OptionalMain.java │ ├── Person.java │ └── ReadPositiveIntParam.java │ ├── chap11 │ ├── AsyncShop.java │ ├── AsyncShopClient.java │ ├── BestPriceFinder.java │ ├── BestPriceFinderMain.java │ ├── Discount.java │ ├── ExchangeService.java │ ├── Quote.java │ ├── Shop.java │ ├── Util.java │ └── v1 │ │ ├── BestPriceFinder.java │ │ ├── BestPriceFinderMain.java │ │ ├── Shop.java │ │ └── ShopMain.java │ ├── chap12 │ └── DateTimeExamples.java │ ├── chap13 │ ├── Recursion.java │ └── SubsetsMain.java │ ├── chap14 │ ├── Combinators.java │ ├── Currying.java │ ├── LazyLists.java │ ├── PatternMatching.java │ ├── PersistentTrainJourney.java │ └── PersistentTree.java │ ├── chap2 │ ├── FilteringApples.java │ └── MeaningOfThis.java │ ├── chap3 │ ├── ExecuteAround.java │ ├── Lambdas.java │ └── Sorting.java │ ├── chap4 │ ├── Dish.java │ ├── StreamBasic.java │ └── StreamVsCollection.java │ ├── chap5 │ ├── BuildingStreams.java │ ├── Filtering.java │ ├── Finding.java │ ├── Laziness.java │ ├── Mapping.java │ ├── NumericStreams.java │ ├── PuttingIntoPractice.java │ ├── Reducing.java │ ├── Trader.java │ └── Transaction.java │ ├── chap6 │ ├── CollectorHarness.java │ ├── Dish.java │ ├── Grouping.java │ ├── GroupingTransactions.java │ ├── PartitionPrimeNumbers.java │ ├── Partitioning.java │ ├── Reducing.java │ ├── Summarizing.java │ └── ToListCollector.java │ ├── chap7 │ ├── ForkJoinSumCalculator.java │ ├── ParallelStreamBenchmark.java │ ├── ParallelStreams.java │ ├── ParallelStreamsHarness.java │ └── WordCount.java │ ├── chap8 │ ├── ChainOfResponsibilityMain.java │ ├── Debugging.java │ ├── FactoryMain.java │ ├── ObserverMain.java │ ├── OnlineBanking.java │ ├── OnlineBankingLambda.java │ ├── Peek.java │ └── StrategyMain.java │ ├── chap9 │ ├── Ambiguous.java │ ├── Diamond.java │ ├── Drawable.java │ ├── Ellipse.java │ ├── Game.java │ ├── Intro.java │ ├── Letter.java │ ├── MostSpecific.java │ ├── README │ ├── Resizable.java │ ├── Square.java │ ├── Triangle.java │ └── Utils.java │ └── dsl │ ├── Grouping.java │ ├── LambdaOrderBuilder.java │ ├── Main.java │ ├── MethodChainingOrderBuilder.java │ ├── Mixed.java │ ├── MixedBuilder.java │ ├── NestedFunctionOrderBuilder.java │ ├── TaxCalculator.java │ └── model │ ├── Order.java │ ├── Stock.java │ ├── Tax.java │ └── Trade.java └── resources └── lambdasinaction ├── chap3 └── data.txt └── chap5 └── data.txt /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /local 3 | 4 | # Eclipse, Netbeans and IntelliJ files 5 | /.* 6 | !.gitignore 7 | /nbproject 8 | /*.ipr 9 | /*.iws 10 | /*.iml 11 | 12 | # Repository wide ignore mac DS_Store files 13 | .DS_Store 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Raoul-Gabriel Urma, Mario Fusco, Alan Mycroft 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Java8InAction 2 | =============== 3 | 4 | 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. 5 | 6 | You can purchase the book here: [http://manning.com/urma/](http://manning.com/urma/) or on Amazon 7 | 8 | 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) 9 | 10 | * Chapter 1: Java 8: why should you care? 11 | * Chapter 2: Passing code with behavior parameterization 12 | * Chapter 3: Lambda expressions 13 | * Chapter 4: Working with Streams 14 | * Chapter 5: Processing data with streams 15 | * Chapter 6: Collecting data with streams 16 | * Chapter 7: Parallel data processing and performance 17 | * Chapter 8: Refactoring, testing, debugging 18 | * Chapter 9: Default methods 19 | * Chapter 10: Using Optional as a better alternative to null 20 | * Chapter 11: CompletableFuture: composable asynchronous programming 21 | * Chapter 12: New Date and Time API 22 | * Chapter 13: Thinking functionally 23 | * Chapter 14: Functional programming techniques 24 | * Chapter 15: Blending OOP and FP: comparing Java 8 and Scala 25 | * Chapter 16: Conclusions and "where next" for Java 26 | * Appendix A: Miscellaneous language updates 27 | * Appendix B: Miscellaneous library updates 28 | * Appendix C: Performing multiple operations in parallel on a Stream 29 | * Appendix D: Lambdas and JVM bytecode 30 | We will update the repository as we update the book. Stay tuned! 31 | 32 | ### Make sure to have JDK8 installed 33 | The latest binary can be found here: http://www.oracle.com/technetwork/java/javase/overview/java8-2100321.html 34 | 35 | $ java -version 36 | 37 | java version "1.8.0_05" 38 | Java(TM) SE Runtime Environment (build 1.8.0_05-b13) 39 | Java HotSpot(TM) 64-Bit Server VM (build 25.5-b02, mixed mode) 40 | 41 | 42 | You can download a preview version here: https://jdk8.java.net/ 43 | 44 | ### Compile/Run the examples 45 | Using maven: 46 | 47 | $ mvn compile 48 | 49 | $ cd target/classes 50 | 51 | $ java lambdasinaction/chap1/FilteringApples 52 | 53 | 54 | Alternatively you can compile the files manually inside the directory src/main/java 55 | 56 | You can also import the project in your favorite IDE: 57 | * In IntelliJ use "File->Open" menu and navigate to the folder where the project resides 58 | * In Eclipse use "File->Import->Existing Maven Projects" (also modify "Reduntant super interfaces" to report as Warnings instead of Errors 59 | * In Netbeans use "File->Open Project" menu -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | manning 8 | lambdasinaction 9 | 1.0 10 | 11 | 12 | UTF-8 13 | 14 | 15 | 16 | 17 | org.openjdk.jmh 18 | jmh-core 19 | 1.17.4 20 | 21 | 22 | org.openjdk.jmh 23 | jmh-generator-annprocess 24 | 1.17.4 25 | 26 | 27 | junit 28 | junit 29 | 4.11 30 | 31 | 32 | 33 | 34 | 35 | 36 | org.apache.maven.plugins 37 | maven-compiler-plugin 38 | 3.1 39 | 40 | 1.9 41 | 1.9 42 | 43 | 44 | 45 | org.apache.maven.plugins 46 | maven-shade-plugin 47 | 48 | 49 | package 50 | 51 | shade 52 | 53 | 54 | benchmarks 55 | 56 | 57 | org.openjdk.jmh.Main 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/appa/Author.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.appa; 2 | 3 | import java.lang.annotation.Repeatable; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | 7 | @Repeatable(Authors.class) 8 | @Retention(RetentionPolicy.RUNTIME) 9 | public @interface Author { 10 | 11 | String name(); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/appa/Authors.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.appa; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | @Retention(RetentionPolicy.RUNTIME) 7 | public @interface Authors { 8 | 9 | Author[] value(); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/appa/Book.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.appa; 2 | 3 | import java.util.Arrays; 4 | 5 | @Author(name = "Raoul") 6 | @Author(name = "Mario") 7 | @Author(name = "Alan") 8 | public class Book { 9 | 10 | public static void main(String[] args) { 11 | Author[] authors = Book.class.getAnnotationsByType(Author.class); 12 | Arrays.asList(authors).stream().forEach(a -> { 13 | System.out.println(a.name()); 14 | }); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/appc/StreamForker.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.appc; 2 | 3 | import java.util.*; 4 | import java.util.concurrent.*; 5 | import java.util.function.*; 6 | import java.util.stream.*; 7 | 8 | /** 9 | * Adapted from http://mail.openjdk.java.net/pipermail/lambda-dev/2013-November/011516.html 10 | */ 11 | public class StreamForker { 12 | 13 | private final Stream stream; 14 | private final Map, ?>> forks = new HashMap<>(); 15 | 16 | public StreamForker(Stream stream) { 17 | this.stream = stream; 18 | } 19 | 20 | public StreamForker fork(Object key, Function, ?> f) { 21 | forks.put(key, f); 22 | return this; 23 | } 24 | 25 | public Results getResults() { 26 | ForkingStreamConsumer consumer = build(); 27 | try { 28 | stream.sequential().forEach(consumer); 29 | } finally { 30 | consumer.finish(); 31 | } 32 | return consumer; 33 | } 34 | 35 | private ForkingStreamConsumer build() { 36 | List> queues = new ArrayList<>(); 37 | 38 | Map> actions = 39 | forks.entrySet().stream().reduce( 40 | new HashMap>(), 41 | (map, e) -> { 42 | map.put(e.getKey(), 43 | getOperationResult(queues, e.getValue())); 44 | return map; 45 | }, 46 | (m1, m2) -> { 47 | m1.putAll(m2); 48 | return m1; 49 | }); 50 | 51 | return new ForkingStreamConsumer<>(queues, actions); 52 | } 53 | 54 | private Future getOperationResult(List> queues, Function, ?> f) { 55 | BlockingQueue queue = new LinkedBlockingQueue<>(); 56 | queues.add(queue); 57 | Spliterator spliterator = new BlockingQueueSpliterator<>(queue); 58 | Stream source = StreamSupport.stream(spliterator, false); 59 | return CompletableFuture.supplyAsync( () -> f.apply(source) ); 60 | } 61 | 62 | public static interface Results { 63 | public R get(Object key); 64 | } 65 | 66 | private static class ForkingStreamConsumer implements Consumer, Results { 67 | static final Object END_OF_STREAM = new Object(); 68 | 69 | private final List> queues; 70 | private final Map> actions; 71 | 72 | ForkingStreamConsumer(List> queues, Map> actions) { 73 | this.queues = queues; 74 | this.actions = actions; 75 | } 76 | 77 | @Override 78 | public void accept(T t) { 79 | queues.forEach(q -> q.add(t)); 80 | } 81 | 82 | @Override 83 | public R get(Object key) { 84 | try { 85 | return ((Future) actions.get(key)).get(); 86 | } catch (Exception e) { 87 | throw new RuntimeException(e); 88 | } 89 | } 90 | 91 | void finish() { 92 | accept((T) END_OF_STREAM); 93 | } 94 | } 95 | 96 | private static class BlockingQueueSpliterator implements Spliterator { 97 | private final BlockingQueue q; 98 | 99 | BlockingQueueSpliterator(BlockingQueue q) { 100 | this.q = q; 101 | } 102 | 103 | @Override 104 | public boolean tryAdvance(Consumer action) { 105 | T t; 106 | while (true) { 107 | try { 108 | t = q.take(); 109 | break; 110 | } 111 | catch (InterruptedException e) { 112 | } 113 | } 114 | 115 | if (t != ForkingStreamConsumer.END_OF_STREAM) { 116 | action.accept(t); 117 | return true; 118 | } 119 | 120 | return false; 121 | } 122 | 123 | @Override 124 | public Spliterator trySplit() { 125 | return null; 126 | } 127 | 128 | @Override 129 | public long estimateSize() { 130 | return 0; 131 | } 132 | 133 | @Override 134 | public int characteristics() { 135 | return 0; 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/appc/StreamForkerExample.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.appc; 2 | 3 | import lambdasinaction.chap6.*; 4 | 5 | import static java.util.stream.Collectors.*; 6 | import static lambdasinaction.chap6.Dish.menu; 7 | 8 | import java.util.*; 9 | import java.util.stream.*; 10 | 11 | public class StreamForkerExample { 12 | 13 | public static void main(String[] args) throws Exception { 14 | processMenu(); 15 | } 16 | 17 | private static void processMenu() { 18 | Stream menuStream = menu.stream(); 19 | 20 | StreamForker.Results results = new StreamForker(menuStream) 21 | .fork("shortMenu", s -> s.map(Dish::getName).collect(joining(", "))) 22 | .fork("totalCalories", s -> s.mapToInt(Dish::getCalories).sum()) 23 | .fork("mostCaloricDish", s -> s.collect( 24 | reducing((d1, d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2)) 25 | .get()) 26 | .fork("dishesByType", s -> s.collect(groupingBy(Dish::getType))) 27 | .getResults(); 28 | 29 | String shortMeny = results.get("shortMenu"); 30 | int totalCalories = results.get("totalCalories"); 31 | Dish mostCaloricDish = results.get("mostCaloricDish"); 32 | Map> dishesByType = results.get("dishesByType"); 33 | 34 | System.out.println("Short menu: " + shortMeny); 35 | System.out.println("Total calories: " + totalCalories); 36 | System.out.println("Most caloric dish: " + mostCaloricDish); 37 | System.out.println("Dishes by type: " + dishesByType); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/appd/InnerClass.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.appd; 2 | 3 | import java.util.function.Function; 4 | 5 | public class InnerClass { 6 | Function f = new Function() { 7 | @Override 8 | public String apply(Object obj) { 9 | return obj.toString(); 10 | } 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/appd/Lambda.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.appd; 2 | 3 | import java.util.function.Function; 4 | 5 | public class Lambda { 6 | Function f = obj -> obj.toString(); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap1/FilteringApples.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap1; 2 | 3 | import java.util.*; 4 | import java.util.function.Predicate; 5 | 6 | public class FilteringApples{ 7 | 8 | public static void main(String ... args){ 9 | 10 | List inventory = Arrays.asList(new Apple(80,"green"), 11 | new Apple(155, "green"), 12 | new Apple(120, "red")); 13 | 14 | // [Apple{color='green', weight=80}, Apple{color='green', weight=155}] 15 | List greenApples = filterApples(inventory, FilteringApples::isGreenApple); 16 | System.out.println(greenApples); 17 | 18 | // [Apple{color='green', weight=155}] 19 | List heavyApples = filterApples(inventory, FilteringApples::isHeavyApple); 20 | System.out.println(heavyApples); 21 | 22 | // [Apple{color='green', weight=80}, Apple{color='green', weight=155}] 23 | List greenApples2 = filterApples(inventory, (Apple a) -> "green".equals(a.getColor())); 24 | System.out.println(greenApples2); 25 | 26 | // [Apple{color='green', weight=155}] 27 | List heavyApples2 = filterApples(inventory, (Apple a) -> a.getWeight() > 150); 28 | System.out.println(heavyApples2); 29 | 30 | // [] 31 | List weirdApples = filterApples(inventory, (Apple a) -> a.getWeight() < 80 || 32 | "brown".equals(a.getColor())); 33 | System.out.println(weirdApples); 34 | } 35 | 36 | public static List filterGreenApples(List inventory){ 37 | List result = new ArrayList<>(); 38 | for (Apple apple: inventory){ 39 | if ("green".equals(apple.getColor())) { 40 | result.add(apple); 41 | } 42 | } 43 | return result; 44 | } 45 | 46 | public static List filterHeavyApples(List inventory){ 47 | List result = new ArrayList<>(); 48 | for (Apple apple: inventory){ 49 | if (apple.getWeight() > 150) { 50 | result.add(apple); 51 | } 52 | } 53 | return result; 54 | } 55 | 56 | public static boolean isGreenApple(Apple apple) { 57 | return "green".equals(apple.getColor()); 58 | } 59 | 60 | public static boolean isHeavyApple(Apple apple) { 61 | return apple.getWeight() > 150; 62 | } 63 | 64 | public static List filterApples(List inventory, Predicate p){ 65 | List result = new ArrayList<>(); 66 | for(Apple apple : inventory){ 67 | if(p.test(apple)){ 68 | result.add(apple); 69 | } 70 | } 71 | return result; 72 | } 73 | 74 | public static class Apple { 75 | private int weight = 0; 76 | private String color = ""; 77 | 78 | public Apple(int weight, String color){ 79 | this.weight = weight; 80 | this.color = color; 81 | } 82 | 83 | public Integer getWeight() { 84 | return weight; 85 | } 86 | 87 | public void setWeight(Integer weight) { 88 | this.weight = weight; 89 | } 90 | 91 | public String getColor() { 92 | return color; 93 | } 94 | 95 | public void setColor(String color) { 96 | this.color = color; 97 | } 98 | 99 | public String toString() { 100 | return "Apple{" + 101 | "color='" + color + '\'' + 102 | ", weight=" + weight + 103 | '}'; 104 | } 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap10/Car.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap10; 2 | 3 | import java.util.*; 4 | 5 | public class Car { 6 | 7 | private Optional insurance; 8 | 9 | public Optional getInsurance() { 10 | return insurance; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap10/Insurance.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap10; 2 | 3 | public class Insurance { 4 | 5 | private String name; 6 | 7 | public String getName() { 8 | return name; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap10/OperationsWithOptional.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap10; 2 | 3 | import java.util.*; 4 | 5 | import static java.util.Optional.of; 6 | import static java.util.Optional.empty; 7 | 8 | public class OperationsWithOptional { 9 | 10 | public static void main(String... args) { 11 | System.out.println(max(of(3), of(5))); 12 | System.out.println(max(empty(), of(5))); 13 | 14 | Optional opt1 = of(5); 15 | Optional opt2 = opt1.or(() -> of(4)); 16 | 17 | System.out.println( 18 | of(5).or(() -> of(4)) 19 | ); 20 | } 21 | 22 | public static final Optional max(Optional i, Optional j) { 23 | return i.flatMap(a -> j.map(b -> Math.max(a, b))); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap10/OptionalMain.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap10; 2 | 3 | import java.util.*; 4 | 5 | import static java.util.stream.Collectors.toSet; 6 | 7 | public class OptionalMain { 8 | 9 | public String getCarInsuranceName(Optional person) { 10 | return person.flatMap(Person::getCar) 11 | .flatMap(Car::getInsurance) 12 | .map(Insurance::getName) 13 | .orElse("Unknown"); 14 | } 15 | 16 | public Set getCarInsuranceNames(List persons) { 17 | return persons.stream() 18 | .map(Person::getCar) 19 | .map(optCar -> optCar.flatMap(Car::getInsurance)) 20 | .map(optInsurance -> optInsurance.map(Insurance::getName)) 21 | .flatMap(Optional::stream) 22 | .collect(toSet()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap10/Person.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap10; 2 | 3 | import java.util.*; 4 | 5 | public class Person { 6 | 7 | private Optional car; 8 | 9 | public Optional getCar() { 10 | return car; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap10/ReadPositiveIntParam.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap10; 2 | 3 | import org.junit.*; 4 | 5 | import java.util.*; 6 | 7 | import static java.util.Optional.*; 8 | import static org.junit.Assert.assertEquals; 9 | 10 | public class ReadPositiveIntParam { 11 | 12 | @Test 13 | public void testMap() { 14 | Properties props = new Properties(); 15 | props.setProperty("a", "5"); 16 | props.setProperty("b", "true"); 17 | props.setProperty("c", "-3"); 18 | 19 | assertEquals(5, readDurationImperative(props, "a")); 20 | assertEquals(0, readDurationImperative(props, "b")); 21 | assertEquals(0, readDurationImperative(props, "c")); 22 | assertEquals(0, readDurationImperative(props, "d")); 23 | 24 | assertEquals(5, readDurationWithOptional(props, "a")); 25 | assertEquals(0, readDurationWithOptional(props, "b")); 26 | assertEquals(0, readDurationWithOptional(props, "c")); 27 | assertEquals(0, readDurationWithOptional(props, "d")); 28 | } 29 | 30 | public static int readDurationImperative(Properties props, String name) { 31 | String value = props.getProperty(name); 32 | if (value != null) { 33 | try { 34 | int i = Integer.parseInt(value); 35 | if (i > 0) { 36 | return i; 37 | } 38 | } catch (NumberFormatException nfe) { } 39 | } 40 | return 0; 41 | } 42 | 43 | public static int readDurationWithOptional(Properties props, String name) { 44 | return ofNullable(props.getProperty(name)) 45 | .flatMap(ReadPositiveIntParam::s2i) 46 | .filter(i -> i > 0).orElse(0); 47 | } 48 | 49 | public static Optional s2i(String s) { 50 | try { 51 | return of(Integer.parseInt(s)); 52 | } catch (NumberFormatException e) { 53 | return empty(); 54 | } 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap11/AsyncShop.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap11; 2 | 3 | import static lambdasinaction.chap11.Util.delay; 4 | import static lambdasinaction.chap11.Util.format; 5 | 6 | import java.util.Random; 7 | import java.util.concurrent.CompletableFuture; 8 | import java.util.concurrent.Future; 9 | 10 | public class AsyncShop { 11 | 12 | private final String name; 13 | private final Random random; 14 | 15 | public AsyncShop(String name) { 16 | this.name = name; 17 | random = new Random(name.charAt(0) * name.charAt(1) * name.charAt(2)); 18 | } 19 | 20 | public Future getPrice(String product) { 21 | /* 22 | CompletableFuture futurePrice = new CompletableFuture<>(); 23 | new Thread( () -> { 24 | try { 25 | double price = calculatePrice(product); 26 | futurePrice.complete(price); 27 | } catch (Exception ex) { 28 | futurePrice.completeExceptionally(ex); 29 | } 30 | }).start(); 31 | return futurePrice; 32 | */ 33 | return CompletableFuture.supplyAsync(() -> calculatePrice(product)); 34 | } 35 | 36 | private double calculatePrice(String product) { 37 | delay(); 38 | if (true) throw new RuntimeException("product not available"); 39 | return format(random.nextDouble() * product.charAt(0) + product.charAt(1)); 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap11/AsyncShopClient.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap11; 2 | 3 | import java.util.concurrent.Future; 4 | 5 | public class AsyncShopClient { 6 | 7 | public static void main(String[] args) { 8 | AsyncShop shop = new AsyncShop("BestShop"); 9 | long start = System.nanoTime(); 10 | Future futurePrice = shop.getPrice("myPhone"); 11 | long incocationTime = ((System.nanoTime() - start) / 1_000_000); 12 | System.out.println("Invocation returned after " + incocationTime + " msecs"); 13 | try { 14 | System.out.println("Price is " + futurePrice.get()); 15 | } catch (Exception e) { 16 | throw new RuntimeException(e); 17 | } 18 | long retrivalTime = ((System.nanoTime() - start) / 1_000_000); 19 | System.out.println("Price returned after " + retrivalTime + " msecs"); 20 | } 21 | } -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap11/BestPriceFinder.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap11; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | import java.util.concurrent.CompletableFuture; 6 | import java.util.concurrent.Executor; 7 | import java.util.concurrent.Executors; 8 | import java.util.concurrent.ThreadFactory; 9 | import java.util.stream.Collectors; 10 | import java.util.stream.Stream; 11 | 12 | public class BestPriceFinder { 13 | 14 | private final List shops = Arrays.asList(new Shop("BestPrice"), 15 | new Shop("LetsSaveBig"), 16 | new Shop("MyFavoriteShop"), 17 | new Shop("BuyItAll"), 18 | new Shop("ShopEasy")); 19 | 20 | private final Executor executor = Executors.newFixedThreadPool(shops.size(), new ThreadFactory() { 21 | @Override 22 | public Thread newThread(Runnable r) { 23 | Thread t = new Thread(r); 24 | t.setDaemon(true); 25 | return t; 26 | } 27 | }); 28 | 29 | public List findPricesSequential(String product) { 30 | return shops.stream() 31 | .map(shop -> shop.getPrice(product)) 32 | .map(Quote::parse) 33 | .map(Discount::applyDiscount) 34 | .collect(Collectors.toList()); 35 | } 36 | 37 | public List findPricesParallel(String product) { 38 | return shops.parallelStream() 39 | .map(shop -> shop.getPrice(product)) 40 | .map(Quote::parse) 41 | .map(Discount::applyDiscount) 42 | .collect(Collectors.toList()); 43 | } 44 | 45 | public List findPricesFuture(String product) { 46 | List> priceFutures = findPricesStream(product) 47 | .collect(Collectors.>toList()); 48 | 49 | return priceFutures.stream() 50 | .map(CompletableFuture::join) 51 | .collect(Collectors.toList()); 52 | } 53 | 54 | public Stream> findPricesStream(String product) { 55 | return shops.stream() 56 | .map(shop -> CompletableFuture.supplyAsync(() -> shop.getPrice(product), executor)) 57 | .map(future -> future.thenApply(Quote::parse)) 58 | .map(future -> future.thenCompose(quote -> CompletableFuture.supplyAsync(() -> Discount.applyDiscount(quote), executor))); 59 | } 60 | 61 | public void printPricesStream(String product) { 62 | long start = System.nanoTime(); 63 | CompletableFuture[] futures = findPricesStream(product) 64 | .map(f -> f.thenAccept(s -> System.out.println(s + " (done in " + ((System.nanoTime() - start) / 1_000_000) + " msecs)"))) 65 | .toArray(size -> new CompletableFuture[size]); 66 | CompletableFuture.allOf(futures).join(); 67 | System.out.println("All shops have now responded in " + ((System.nanoTime() - start) / 1_000_000) + " msecs"); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap11/BestPriceFinderMain.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap11; 2 | 3 | import java.util.List; 4 | import java.util.function.Supplier; 5 | 6 | public class BestPriceFinderMain { 7 | 8 | private static BestPriceFinder bestPriceFinder = new BestPriceFinder(); 9 | 10 | public static void main(String[] args) { 11 | execute("sequential", () -> bestPriceFinder.findPricesSequential("myPhone27S")); 12 | execute("parallel", () -> bestPriceFinder.findPricesParallel("myPhone27S")); 13 | execute("composed CompletableFuture", () -> bestPriceFinder.findPricesFuture("myPhone27S")); 14 | bestPriceFinder.printPricesStream("myPhone27S"); 15 | } 16 | 17 | private static void execute(String msg, Supplier> s) { 18 | long start = System.nanoTime(); 19 | System.out.println(s.get()); 20 | long duration = (System.nanoTime() - start) / 1_000_000; 21 | System.out.println(msg + " done in " + duration + " msecs"); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap11/Discount.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap11; 2 | 3 | import static lambdasinaction.chap11.Util.delay; 4 | import static lambdasinaction.chap11.Util.format; 5 | 6 | public class Discount { 7 | 8 | public enum Code { 9 | NONE(0), SILVER(5), GOLD(10), PLATINUM(15), DIAMOND(20); 10 | 11 | private final int percentage; 12 | 13 | Code(int percentage) { 14 | this.percentage = percentage; 15 | } 16 | } 17 | 18 | public static String applyDiscount(Quote quote) { 19 | return quote.getShopName() + " price is " + Discount.apply(quote.getPrice(), quote.getDiscountCode()); 20 | } 21 | private static double apply(double price, Code code) { 22 | delay(); 23 | return format(price * (100 - code.percentage) / 100); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap11/ExchangeService.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap11; 2 | 3 | import static lambdasinaction.chap11.Util.delay; 4 | 5 | public class ExchangeService { 6 | 7 | public enum Money { 8 | USD(1.0), EUR(1.35387), GBP(1.69715), CAD(.92106), MXN(.07683); 9 | 10 | private final double rate; 11 | 12 | Money(double rate) { 13 | this.rate = rate; 14 | } 15 | } 16 | 17 | public static double getRate(Money source, Money destination) { 18 | return getRateWithDelay(source, destination); 19 | } 20 | 21 | private static double getRateWithDelay(Money source, Money destination) { 22 | delay(); 23 | return destination.rate / source.rate; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap11/Quote.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap11; 2 | 3 | public class Quote { 4 | 5 | private final String shopName; 6 | private final double price; 7 | private final Discount.Code discountCode; 8 | 9 | public Quote(String shopName, double price, Discount.Code discountCode) { 10 | this.shopName = shopName; 11 | this.price = price; 12 | this.discountCode = discountCode; 13 | } 14 | 15 | public static Quote parse(String s) { 16 | String[] split = s.split(":"); 17 | String shopName = split[0]; 18 | double price = Double.parseDouble(split[1]); 19 | Discount.Code discountCode = Discount.Code.valueOf(split[2]); 20 | return new Quote(shopName, price, discountCode); 21 | } 22 | 23 | public String getShopName() { 24 | return shopName; 25 | } 26 | 27 | public double getPrice() { 28 | return price; 29 | } 30 | 31 | public Discount.Code getDiscountCode() { 32 | return discountCode; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap11/Shop.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap11; 2 | 3 | import static lambdasinaction.chap11.Util.delay; 4 | import static lambdasinaction.chap11.Util.format; 5 | 6 | import java.util.Random; 7 | 8 | public class Shop { 9 | 10 | private final String name; 11 | private final Random random; 12 | 13 | public Shop(String name) { 14 | this.name = name; 15 | random = new Random(name.charAt(0) * name.charAt(1) * name.charAt(2)); 16 | } 17 | 18 | public String getPrice(String product) { 19 | double price = calculatePrice(product); 20 | Discount.Code code = Discount.Code.values()[random.nextInt(Discount.Code.values().length)]; 21 | return name + ":" + price + ":" + code; 22 | } 23 | 24 | public double calculatePrice(String product) { 25 | delay(); 26 | return format(random.nextDouble() * product.charAt(0) + product.charAt(1)); 27 | } 28 | 29 | public String getName() { 30 | return name; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap11/Util.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap11; 2 | 3 | import java.text.DecimalFormat; 4 | import java.text.DecimalFormatSymbols; 5 | import java.util.List; 6 | import java.util.Locale; 7 | import java.util.Random; 8 | import java.util.concurrent.CompletableFuture; 9 | import java.util.stream.Collectors; 10 | 11 | public class Util { 12 | 13 | private static final Random RANDOM = new Random(0); 14 | private static final DecimalFormat formatter = new DecimalFormat("#.##", new DecimalFormatSymbols(Locale.US)); 15 | 16 | public static void delay() { 17 | int delay = 1000; 18 | //int delay = 500 + RANDOM.nextInt(2000); 19 | try { 20 | Thread.sleep(delay); 21 | } catch (InterruptedException e) { 22 | throw new RuntimeException(e); 23 | } 24 | } 25 | 26 | public static double format(double number) { 27 | synchronized (formatter) { 28 | return new Double(formatter.format(number)); 29 | } 30 | } 31 | 32 | public static CompletableFuture> sequence(List> futures) { 33 | /* 34 | CompletableFuture allDoneFuture = 35 | CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])); 36 | return allDoneFuture.thenApply(v -> 37 | futures.stream(). 38 | map(future -> future.join()). 39 | collect(Collectors.toList()) 40 | ); 41 | */ 42 | return CompletableFuture.supplyAsync(() -> futures.stream(). 43 | map(future -> future.join()). 44 | collect(Collectors.toList())); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap11/v1/BestPriceFinder.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap11.v1; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | import java.util.concurrent.Callable; 7 | import java.util.concurrent.CompletableFuture; 8 | import java.util.concurrent.ExecutionException; 9 | import java.util.concurrent.Executor; 10 | import java.util.concurrent.ExecutorService; 11 | import java.util.concurrent.Executors; 12 | import java.util.concurrent.Future; 13 | import java.util.concurrent.ThreadFactory; 14 | import java.util.stream.Collectors; 15 | import java.util.stream.Stream; 16 | 17 | import lambdasinaction.chap11.ExchangeService; 18 | import lambdasinaction.chap11.ExchangeService.Money; 19 | 20 | public class BestPriceFinder { 21 | 22 | private final List shops = Arrays.asList(new Shop("BestPrice"), 23 | new Shop("LetsSaveBig"), 24 | new Shop("MyFavoriteShop"), 25 | new Shop("BuyItAll")/*, 26 | new Shop("ShopEasy")*/); 27 | 28 | private final Executor executor = Executors.newFixedThreadPool(shops.size(), new ThreadFactory() { 29 | @Override 30 | public Thread newThread(Runnable r) { 31 | Thread t = new Thread(r); 32 | t.setDaemon(true); 33 | return t; 34 | } 35 | }); 36 | 37 | public List findPricesSequential(String product) { 38 | return shops.stream() 39 | .map(shop -> shop.getName() + " price is " + shop.getPrice(product)) 40 | .collect(Collectors.toList()); 41 | } 42 | 43 | public List findPricesParallel(String product) { 44 | return shops.parallelStream() 45 | .map(shop -> shop.getName() + " price is " + shop.getPrice(product)) 46 | .collect(Collectors.toList()); 47 | } 48 | 49 | public List findPricesFuture(String product) { 50 | List> priceFutures = 51 | shops.stream() 52 | .map(shop -> CompletableFuture.supplyAsync(() -> shop.getName() + " price is " 53 | + shop.getPrice(product), executor)) 54 | .collect(Collectors.toList()); 55 | 56 | List prices = priceFutures.stream() 57 | .map(CompletableFuture::join) 58 | .collect(Collectors.toList()); 59 | return prices; 60 | } 61 | 62 | public List findPricesInUSD(String product) { 63 | List> priceFutures = new ArrayList<>(); 64 | for (Shop shop : shops) { 65 | // Start of Listing 10.20. 66 | // Only the type of futurePriceInUSD has been changed to 67 | // CompletableFuture so that it is compatible with the 68 | // CompletableFuture::join operation below. 69 | CompletableFuture futurePriceInUSD = 70 | CompletableFuture.supplyAsync(() -> shop.getPrice(product)) 71 | .thenCombine( 72 | CompletableFuture.supplyAsync( 73 | () -> ExchangeService.getRate(Money.EUR, Money.USD)), 74 | (price, rate) -> price * rate 75 | ); 76 | priceFutures.add(futurePriceInUSD); 77 | } 78 | // Drawback: The shop is not accessible anymore outside the loop, 79 | // so the getName() call below has been commented out. 80 | List prices = priceFutures 81 | .stream() 82 | .map(CompletableFuture::join) 83 | .map(price -> /*shop.getName() +*/ " price is " + price) 84 | .collect(Collectors.toList()); 85 | return prices; 86 | } 87 | 88 | public List findPricesInUSDJava7(String product) { 89 | ExecutorService executor = Executors.newCachedThreadPool(); 90 | List> priceFutures = new ArrayList<>(); 91 | for (Shop shop : shops) { 92 | final Future futureRate = executor.submit(new Callable() { 93 | public Double call() { 94 | return ExchangeService.getRate(Money.EUR, Money.USD); 95 | } 96 | }); 97 | Future futurePriceInUSD = executor.submit(new Callable() { 98 | public Double call() { 99 | try { 100 | double priceInEUR = shop.getPrice(product); 101 | return priceInEUR * futureRate.get(); 102 | } catch (InterruptedException | ExecutionException e) { 103 | throw new RuntimeException(e.getMessage(), e); 104 | } 105 | } 106 | }); 107 | priceFutures.add(futurePriceInUSD); 108 | } 109 | List prices = new ArrayList<>(); 110 | for (Future priceFuture : priceFutures) { 111 | try { 112 | prices.add(/*shop.getName() +*/ " price is " + priceFuture.get()); 113 | } 114 | catch (ExecutionException | InterruptedException e) { 115 | e.printStackTrace(); 116 | } 117 | } 118 | return prices; 119 | } 120 | 121 | public List findPricesInUSD2(String product) { 122 | List> priceFutures = new ArrayList<>(); 123 | for (Shop shop : shops) { 124 | // Here, an extra operation has been added so that the shop name 125 | // is retrieved within the loop. As a result, we now deal with 126 | // CompletableFuture instances. 127 | CompletableFuture futurePriceInUSD = 128 | CompletableFuture.supplyAsync(() -> shop.getPrice(product)) 129 | .thenCombine( 130 | CompletableFuture.supplyAsync( 131 | () -> ExchangeService.getRate(Money.EUR, Money.USD)), 132 | (price, rate) -> price * rate 133 | ).thenApply(price -> shop.getName() + " price is " + price); 134 | priceFutures.add(futurePriceInUSD); 135 | } 136 | List prices = priceFutures 137 | .stream() 138 | .map(CompletableFuture::join) 139 | .collect(Collectors.toList()); 140 | return prices; 141 | } 142 | 143 | public List findPricesInUSD3(String product) { 144 | // Here, the for loop has been replaced by a mapping function... 145 | Stream> priceFuturesStream = shops 146 | .stream() 147 | .map(shop -> CompletableFuture 148 | .supplyAsync(() -> shop.getPrice(product)) 149 | .thenCombine( 150 | CompletableFuture.supplyAsync(() -> ExchangeService.getRate(Money.EUR, Money.USD)), 151 | (price, rate) -> price * rate) 152 | .thenApply(price -> shop.getName() + " price is " + price)); 153 | // However, we should gather the CompletableFutures into a List so that the asynchronous 154 | // operations are triggered before being "joined." 155 | List> priceFutures = priceFuturesStream.collect(Collectors.toList()); 156 | List prices = priceFutures 157 | .stream() 158 | .map(CompletableFuture::join) 159 | .collect(Collectors.toList()); 160 | return prices; 161 | } 162 | 163 | } 164 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap11/v1/BestPriceFinderMain.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap11.v1; 2 | 3 | import java.util.List; 4 | import java.util.function.Supplier; 5 | 6 | public class BestPriceFinderMain { 7 | 8 | private static BestPriceFinder bestPriceFinder = new BestPriceFinder(); 9 | 10 | public static void main(String[] args) { 11 | execute("sequential", () -> bestPriceFinder.findPricesSequential("myPhone27S")); 12 | execute("parallel", () -> bestPriceFinder.findPricesParallel("myPhone27S")); 13 | execute("composed CompletableFuture", () -> bestPriceFinder.findPricesFuture("myPhone27S")); 14 | execute("combined USD CompletableFuture", () -> bestPriceFinder.findPricesInUSD("myPhone27S")); 15 | execute("combined USD CompletableFuture v2", () -> bestPriceFinder.findPricesInUSD2("myPhone27S")); 16 | execute("combined USD CompletableFuture v3", () -> bestPriceFinder.findPricesInUSD3("myPhone27S")); 17 | } 18 | 19 | private static void execute(String msg, Supplier> s) { 20 | long start = System.nanoTime(); 21 | System.out.println(s.get()); 22 | long duration = (System.nanoTime() - start) / 1_000_000; 23 | System.out.println(msg + " done in " + duration + " msecs"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap11/v1/Shop.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap11.v1; 2 | 3 | import static lambdasinaction.chap11.Util.delay; 4 | 5 | import java.util.Random; 6 | import java.util.concurrent.CompletableFuture; 7 | import java.util.concurrent.Future; 8 | 9 | public class Shop { 10 | 11 | private final String name; 12 | private final Random random; 13 | 14 | public Shop(String name) { 15 | this.name = name; 16 | random = new Random(name.charAt(0) * name.charAt(1) * name.charAt(2)); 17 | } 18 | 19 | public double getPrice(String product) { 20 | return calculatePrice(product); 21 | } 22 | 23 | private double calculatePrice(String product) { 24 | delay(); 25 | return random.nextDouble() * product.charAt(0) + product.charAt(1); 26 | } 27 | 28 | public Future getPriceAsync(String product) { 29 | CompletableFuture futurePrice = new CompletableFuture<>(); 30 | new Thread( () -> { 31 | double price = calculatePrice(product); 32 | futurePrice.complete(price); 33 | }).start(); 34 | return futurePrice; 35 | } 36 | 37 | public String getName() { 38 | return name; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap11/v1/ShopMain.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap11.v1; 2 | 3 | import java.util.concurrent.ExecutionException; 4 | import java.util.concurrent.Future; 5 | 6 | public class ShopMain { 7 | 8 | public static void main(String[] args) { 9 | Shop shop = new Shop("BestShop"); 10 | long start = System.nanoTime(); 11 | Future futurePrice = shop.getPriceAsync("my favorite product"); 12 | long invocationTime = ((System.nanoTime() - start) / 1_000_000); 13 | System.out.println("Invocation returned after " + invocationTime 14 | + " msecs"); 15 | // Do some more tasks, like querying other shops 16 | doSomethingElse(); 17 | // while the price of the product is being calculated 18 | try { 19 | double price = futurePrice.get(); 20 | System.out.printf("Price is %.2f%n", price); 21 | } catch (ExecutionException | InterruptedException e) { 22 | throw new RuntimeException(e); 23 | } 24 | long retrievalTime = ((System.nanoTime() - start) / 1_000_000); 25 | System.out.println("Price returned after " + retrievalTime + " msecs"); 26 | } 27 | 28 | private static void doSomethingElse() { 29 | System.out.println("Doing something else..."); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap12/DateTimeExamples.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap12; 2 | 3 | import static java.time.temporal.TemporalAdjusters.lastDayOfMonth; 4 | import static java.time.temporal.TemporalAdjusters.nextOrSame; 5 | 6 | import java.text.DateFormat; 7 | import java.text.SimpleDateFormat; 8 | import java.time.DayOfWeek; 9 | import java.time.Duration; 10 | import java.time.Instant; 11 | import java.time.LocalDate; 12 | import java.time.LocalDateTime; 13 | import java.time.LocalTime; 14 | import java.time.Month; 15 | import java.time.chrono.JapaneseDate; 16 | import java.time.format.DateTimeFormatter; 17 | import java.time.format.DateTimeFormatterBuilder; 18 | import java.time.temporal.ChronoField; 19 | import java.time.temporal.ChronoUnit; 20 | import java.time.temporal.Temporal; 21 | import java.time.temporal.TemporalAdjuster; 22 | import java.util.Calendar; 23 | import java.util.Date; 24 | import java.util.Locale; 25 | 26 | public class DateTimeExamples { 27 | 28 | private static final ThreadLocal formatters = new ThreadLocal() { 29 | protected DateFormat initialValue() { 30 | return new SimpleDateFormat("dd-MMM-yyyy"); 31 | } 32 | }; 33 | 34 | public static void main(String[] args) { 35 | useOldDate(); 36 | useLocalDate(); 37 | useTemporalAdjuster(); 38 | useDateFormatter(); 39 | } 40 | 41 | private static void useOldDate() { 42 | Date date = new Date(114, 2, 18); 43 | System.out.println(date); 44 | 45 | System.out.println(formatters.get().format(date)); 46 | 47 | Calendar calendar = Calendar.getInstance(); 48 | calendar.set(2014, Calendar.FEBRUARY, 18); 49 | System.out.println(calendar); 50 | } 51 | 52 | private static void useLocalDate() { 53 | LocalDate date = LocalDate.of(2014, 3, 18); 54 | int year = date.getYear(); // 2014 55 | Month month = date.getMonth(); // MARCH 56 | int day = date.getDayOfMonth(); // 18 57 | DayOfWeek dow = date.getDayOfWeek(); // TUESDAY 58 | int len = date.lengthOfMonth(); // 31 (days in March) 59 | boolean leap = date.isLeapYear(); // false (not a leap year) 60 | System.out.println(date); 61 | 62 | int y = date.get(ChronoField.YEAR); 63 | int m = date.get(ChronoField.MONTH_OF_YEAR); 64 | int d = date.get(ChronoField.DAY_OF_MONTH); 65 | 66 | LocalTime time = LocalTime.of(13, 45, 20); // 13:45:20 67 | int hour = time.getHour(); // 13 68 | int minute = time.getMinute(); // 45 69 | int second = time.getSecond(); // 20 70 | System.out.println(time); 71 | 72 | LocalDateTime dt1 = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45, 20); // 2014-03-18T13:45 73 | LocalDateTime dt2 = LocalDateTime.of(date, time); 74 | LocalDateTime dt3 = date.atTime(13, 45, 20); 75 | LocalDateTime dt4 = date.atTime(time); 76 | LocalDateTime dt5 = time.atDate(date); 77 | System.out.println(dt1); 78 | 79 | LocalDate date1 = dt1.toLocalDate(); 80 | System.out.println(date1); 81 | LocalTime time1 = dt1.toLocalTime(); 82 | System.out.println(time1); 83 | 84 | Instant instant = Instant.ofEpochSecond(44 * 365 * 86400); 85 | Instant now = Instant.now(); 86 | 87 | Duration d1 = Duration.between(LocalTime.of(13, 45, 10), time); 88 | Duration d2 = Duration.between(instant, now); 89 | System.out.println(d1.getSeconds()); 90 | System.out.println(d2.getSeconds()); 91 | 92 | Duration threeMinutes = Duration.of(3, ChronoUnit.MINUTES); 93 | System.out.println(threeMinutes); 94 | 95 | JapaneseDate japaneseDate = JapaneseDate.from(date); 96 | System.out.println(japaneseDate); 97 | } 98 | 99 | private static void useTemporalAdjuster() { 100 | LocalDate date = LocalDate.of(2014, 3, 18); 101 | date = date.with(nextOrSame(DayOfWeek.SUNDAY)); 102 | System.out.println(date); 103 | date = date.with(lastDayOfMonth()); 104 | System.out.println(date); 105 | 106 | date = date.with(new NextWorkingDay()); 107 | System.out.println(date); 108 | date = date.with(nextOrSame(DayOfWeek.FRIDAY)); 109 | System.out.println(date); 110 | date = date.with(new NextWorkingDay()); 111 | System.out.println(date); 112 | 113 | date = date.with(nextOrSame(DayOfWeek.FRIDAY)); 114 | System.out.println(date); 115 | date = date.with(temporal -> { 116 | DayOfWeek dow = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK)); 117 | int dayToAdd = 1; 118 | if (dow == DayOfWeek.FRIDAY) dayToAdd = 3; 119 | if (dow == DayOfWeek.SATURDAY) dayToAdd = 2; 120 | return temporal.plus(dayToAdd, ChronoUnit.DAYS); 121 | }); 122 | System.out.println(date); 123 | } 124 | 125 | private static class NextWorkingDay implements TemporalAdjuster { 126 | @Override 127 | public Temporal adjustInto(Temporal temporal) { 128 | DayOfWeek dow = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK)); 129 | int dayToAdd = 1; 130 | if (dow == DayOfWeek.FRIDAY) dayToAdd = 3; 131 | if (dow == DayOfWeek.SATURDAY) dayToAdd = 2; 132 | return temporal.plus(dayToAdd, ChronoUnit.DAYS); 133 | } 134 | } 135 | 136 | private static void useDateFormatter() { 137 | LocalDate date = LocalDate.of(2014, 3, 18); 138 | DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy"); 139 | DateTimeFormatter italianFormatter = DateTimeFormatter.ofPattern("d. MMMM yyyy", Locale.ITALIAN); 140 | 141 | System.out.println(date.format(DateTimeFormatter.ISO_LOCAL_DATE)); 142 | System.out.println(date.format(formatter)); 143 | System.out.println(date.format(italianFormatter)); 144 | 145 | DateTimeFormatter complexFormatter = new DateTimeFormatterBuilder() 146 | .appendText(ChronoField.DAY_OF_MONTH) 147 | .appendLiteral(". ") 148 | .appendText(ChronoField.MONTH_OF_YEAR) 149 | .appendLiteral(" ") 150 | .appendText(ChronoField.YEAR) 151 | .parseCaseInsensitive() 152 | .toFormatter(Locale.ITALIAN); 153 | 154 | System.out.println(date.format(complexFormatter)); 155 | } 156 | 157 | } 158 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap13/Recursion.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap13; 2 | 3 | import java.util.stream.LongStream; 4 | 5 | 6 | public class Recursion { 7 | 8 | public static void main(String[] args) { 9 | System.out.println(factorialIterative(5)); 10 | System.out.println(factorialRecursive(5)); 11 | System.out.println(factorialStreams(5)); 12 | System.out.println(factorialTailRecursive(5)); 13 | } 14 | 15 | public static int factorialIterative(int n) { 16 | int r = 1; 17 | for (int i = 1; i <= n; i++) { 18 | r*=i; 19 | } 20 | return r; 21 | } 22 | 23 | public static long factorialRecursive(long n) { 24 | return n == 1 ? 1 : n*factorialRecursive(n-1); 25 | } 26 | 27 | public static long factorialStreams(long n){ 28 | return LongStream.rangeClosed(1, n) 29 | .reduce(1, (long a, long b) -> a * b); 30 | } 31 | 32 | public static long factorialTailRecursive(long n) { 33 | return factorialHelper(1, n); 34 | } 35 | 36 | public static long factorialHelper(long acc, long n) { 37 | return n == 1 ? acc : factorialHelper(acc * n, n-1); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap13/SubsetsMain.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap13; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.Collections; 6 | import java.util.List; 7 | 8 | public class SubsetsMain { 9 | 10 | public static void main(String[] args) { 11 | List> subs = subsets(Arrays.asList(1, 4, 9)); 12 | subs.forEach(System.out::println); 13 | } 14 | 15 | public static List> subsets(List l) { 16 | if (l.isEmpty()) { 17 | List> ans = new ArrayList<>(); 18 | ans.add(Collections.emptyList()); 19 | return ans; 20 | } 21 | Integer first = l.get(0); 22 | List rest = l.subList(1,l.size()); 23 | List> subans = subsets(rest); 24 | List> subans2 = insertAll(first, subans); 25 | return concat(subans, subans2); 26 | } 27 | 28 | public static List> insertAll(Integer first, List> lists) { 29 | List> result = new ArrayList<>(); 30 | for (List l : lists) { 31 | List copyList = new ArrayList<>(); 32 | copyList.add(first); 33 | copyList.addAll(l); 34 | result.add(copyList); 35 | } 36 | return result; 37 | } 38 | 39 | static List> concat(List> a, List> b) { 40 | List> r = new ArrayList<>(a); 41 | r.addAll(b); 42 | return r; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap14/Combinators.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap14; 2 | 3 | import java.util.function.Function; 4 | 5 | public class Combinators { 6 | 7 | public static void main(String[] args) { 8 | System.out.println(repeat(3, (Integer x) -> 2 * x).apply(10)); 9 | } 10 | 11 | static Function compose(Function g, Function f) { 12 | return x -> g.apply(f.apply(x)); 13 | } 14 | 15 | static Function repeat(int n, Function f) { 16 | return n == 0 ? x -> x : compose(f, repeat(n - 1, f)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap14/Currying.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap14; 2 | 3 | import java.util.function.DoubleUnaryOperator; 4 | 5 | 6 | public class Currying { 7 | 8 | public static void main(String[] args) { 9 | DoubleUnaryOperator convertCtoF = curriedConverter(9.0/5, 32); 10 | DoubleUnaryOperator convertUSDtoGBP = curriedConverter(0.6, 0); 11 | DoubleUnaryOperator convertKmtoMi = curriedConverter(0.6214, 0); 12 | 13 | System.out.println(convertCtoF.applyAsDouble(24)); 14 | System.out.println(convertUSDtoGBP.applyAsDouble(100)); 15 | System.out.println(convertKmtoMi.applyAsDouble(20)); 16 | 17 | DoubleUnaryOperator convertFtoC = expandedCurriedConverter(-32, 5.0/9, 0); 18 | System.out.println(convertFtoC.applyAsDouble(98.6)); 19 | } 20 | 21 | static double converter(double x, double y, double z) { 22 | return x * y + z; 23 | } 24 | 25 | static DoubleUnaryOperator curriedConverter(double y, double z) { 26 | return (double x) -> x * y + z; 27 | } 28 | 29 | static DoubleUnaryOperator expandedCurriedConverter(double w, double y, double z) { 30 | return (double x) -> (x + w) * y + z; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap14/LazyLists.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap14; 2 | 3 | import java.util.function.Supplier; 4 | import java.util.function.Predicate; 5 | 6 | public class LazyLists { 7 | 8 | public static void main(String[] args) { 9 | MyList l = new MyLinkedList<>(5, new MyLinkedList<>(10, 10 | new Empty())); 11 | 12 | System.out.println(l.head()); 13 | 14 | LazyList numbers = from(2); 15 | int two = numbers.head(); 16 | int three = numbers.tail().head(); 17 | int four = numbers.tail().tail().head(); 18 | System.out.println(two + " " + three + " " + four); 19 | 20 | numbers = from(2); 21 | int prime_two = primes(numbers).head(); 22 | int prime_three = primes(numbers).tail().head(); 23 | int prime_five = primes(numbers).tail().tail().head(); 24 | System.out.println(prime_two + " " + prime_three + " " + prime_five); 25 | 26 | // this will run until a stackoverflow occur because Java does not 27 | // support tail call elimination 28 | // printAll(primes(from(2))); 29 | } 30 | 31 | interface MyList { 32 | T head(); 33 | 34 | MyList tail(); 35 | 36 | default boolean isEmpty() { 37 | return true; 38 | } 39 | 40 | MyList filter(Predicate p); 41 | } 42 | 43 | static class MyLinkedList implements MyList { 44 | final T head; 45 | final MyList tail; 46 | 47 | public MyLinkedList(T head, MyList tail) { 48 | this.head = head; 49 | this.tail = tail; 50 | } 51 | 52 | public T head() { 53 | return head; 54 | } 55 | 56 | public MyList tail() { 57 | return tail; 58 | } 59 | 60 | public boolean isEmpty() { 61 | return false; 62 | } 63 | 64 | public MyList filter(Predicate p) { 65 | return isEmpty() ? this : p.test(head()) ? new MyLinkedList<>( 66 | head(), tail().filter(p)) : tail().filter(p); 67 | } 68 | } 69 | 70 | static class Empty implements MyList { 71 | public T head() { 72 | throw new UnsupportedOperationException(); 73 | } 74 | 75 | public MyList tail() { 76 | throw new UnsupportedOperationException(); 77 | } 78 | 79 | public MyList filter(Predicate p) { 80 | return this; 81 | } 82 | } 83 | 84 | static class LazyList implements MyList { 85 | final T head; 86 | final Supplier> tail; 87 | 88 | public LazyList(T head, Supplier> tail) { 89 | this.head = head; 90 | this.tail = tail; 91 | } 92 | 93 | public T head() { 94 | return head; 95 | } 96 | 97 | public MyList tail() { 98 | return tail.get(); 99 | } 100 | 101 | public boolean isEmpty() { 102 | return false; 103 | } 104 | 105 | public MyList filter(Predicate p) { 106 | return isEmpty() ? this : p.test(head()) ? new LazyList<>(head(), 107 | () -> tail().filter(p)) : tail().filter(p); 108 | } 109 | 110 | } 111 | 112 | public static LazyList from(int n) { 113 | return new LazyList(n, () -> from(n + 1)); 114 | } 115 | 116 | public static MyList primes(MyList numbers) { 117 | return new LazyList<>(numbers.head(), () -> primes(numbers.tail() 118 | .filter(n -> n % numbers.head() != 0))); 119 | } 120 | 121 | static void printAll(MyList numbers) { 122 | if (numbers.isEmpty()) { 123 | return; 124 | } 125 | System.out.println(numbers.head()); 126 | printAll(numbers.tail()); 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap14/PatternMatching.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap14; 2 | 3 | import java.util.function.Function; 4 | import java.util.function.Supplier; 5 | 6 | public class PatternMatching { 7 | 8 | public static void main(String[] args) { 9 | simplify(); 10 | 11 | Expr e = new BinOp("+", new Number(5), new BinOp("*", new Number(3), new Number(4))); 12 | Integer result = evaluate(e); 13 | System.out.println(e + " = " + result); 14 | } 15 | 16 | private static void simplify() { 17 | TriFunction binopcase = 18 | (opname, left, right) -> { 19 | if ("+".equals(opname)) { 20 | if (left instanceof Number && ((Number) left).val == 0) { 21 | return right; 22 | } 23 | if (right instanceof Number && ((Number) right).val == 0) { 24 | return left; 25 | } 26 | } 27 | if ("*".equals(opname)) { 28 | if (left instanceof Number && ((Number) left).val == 1) { 29 | return right; 30 | } 31 | if (right instanceof Number && ((Number) right).val == 1) { 32 | return left; 33 | } 34 | } 35 | return new BinOp(opname, left, right); 36 | }; 37 | Function numcase = val -> new Number(val); 38 | Supplier defaultcase = () -> new Number(0); 39 | 40 | Expr e = new BinOp("+", new Number(5), new Number(0)); 41 | Expr match = patternMatchExpr(e, binopcase, numcase, defaultcase); 42 | if (match instanceof Number) { 43 | System.out.println("Number: " + match); 44 | } else if (match instanceof BinOp) { 45 | System.out.println("BinOp: " + match); 46 | } 47 | } 48 | 49 | private static Integer evaluate(Expr e) { 50 | Function numcase = val -> val; 51 | Supplier defaultcase = () -> 0; 52 | TriFunction binopcase = 53 | (opname, left, right) -> { 54 | if ("+".equals(opname)) { 55 | if (left instanceof Number && right instanceof Number) { 56 | return ((Number) left).val + ((Number) right).val; 57 | } 58 | if (right instanceof Number && left instanceof BinOp) { 59 | return ((Number) right).val + evaluate((BinOp) left); 60 | } 61 | if (left instanceof Number && right instanceof BinOp) { 62 | return ((Number) left).val + evaluate((BinOp) right); 63 | } 64 | if (left instanceof BinOp && right instanceof BinOp) { 65 | return evaluate((BinOp) left) + evaluate((BinOp) right); 66 | } 67 | } 68 | if ("*".equals(opname)) { 69 | if (left instanceof Number && right instanceof Number) { 70 | return ((Number) left).val * ((Number) right).val; 71 | } 72 | if (right instanceof Number && left instanceof BinOp) { 73 | return ((Number) right).val * evaluate((BinOp) left); 74 | } 75 | if (left instanceof Number && right instanceof BinOp) { 76 | return ((Number) left).val * evaluate((BinOp) right); 77 | } 78 | if (left instanceof BinOp && right instanceof BinOp) { 79 | return evaluate((BinOp) left) * evaluate((BinOp) right); 80 | } 81 | } 82 | return defaultcase.get(); 83 | }; 84 | 85 | return patternMatchExpr(e, binopcase, numcase, defaultcase); 86 | } 87 | 88 | static class Expr { 89 | } 90 | 91 | static class Number extends Expr { 92 | int val; 93 | public Number(int val) { 94 | this.val = val; 95 | } 96 | 97 | @Override 98 | public String toString() { 99 | return "" + val; 100 | } 101 | } 102 | 103 | static class BinOp extends Expr { 104 | String opname; 105 | Expr left, right; 106 | public BinOp(String opname, Expr left, Expr right) { 107 | this.opname = opname; 108 | this.left = left; 109 | this.right = right; 110 | } 111 | 112 | @Override 113 | public String toString() { 114 | return "(" + left + " " + opname + " " + right + ")"; 115 | } 116 | } 117 | 118 | static T MyIf(boolean b, Supplier truecase, Supplier falsecase) { 119 | return b ? truecase.get() : falsecase.get(); 120 | } 121 | 122 | static interface TriFunction { 123 | R apply(S s, T t, U u); 124 | } 125 | 126 | static T patternMatchExpr(Expr e, 127 | TriFunction binopcase, 128 | Function numcase, Supplier defaultcase) { 129 | 130 | if (e instanceof BinOp) { 131 | return binopcase.apply(((BinOp) e).opname, ((BinOp) e).left, ((BinOp) e).right); 132 | } else if (e instanceof Number) { 133 | return numcase.apply(((Number) e).val); 134 | } else { 135 | return defaultcase.get(); 136 | } 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap14/PersistentTrainJourney.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap14; 2 | 3 | import java.util.function.Consumer; 4 | 5 | public class PersistentTrainJourney { 6 | 7 | public static void main(String[] args) { 8 | TrainJourney tj1 = new TrainJourney(40, new TrainJourney(30, null)); 9 | TrainJourney tj2 = new TrainJourney(20, new TrainJourney(50, null)); 10 | 11 | TrainJourney appended = append(tj1, tj2); 12 | visit(appended, tj -> { System.out.print(tj.price + " - "); }); 13 | System.out.println(); 14 | 15 | // A new TrainJourney is created without altering tj1 and tj2. 16 | TrainJourney appended2 = append(tj1, tj2); 17 | visit(appended2, tj -> { System.out.print(tj.price + " - "); }); 18 | System.out.println(); 19 | 20 | // tj1 is altered but it's still not visible in the results. 21 | TrainJourney linked = link(tj1, tj2); 22 | visit(linked, tj -> { System.out.print(tj.price + " - "); }); 23 | System.out.println(); 24 | 25 | // ... but here, if this code is uncommented, tj2 will be appended 26 | // at the end of the already altered tj1. This will cause a 27 | // StackOverflowError from the endless visit() recursive calls on 28 | // the tj2 part of the twice altered tj1. 29 | /*TrainJourney linked2 = link(tj1, tj2); 30 | visit(linked2, tj -> { System.out.print(tj.price + " - "); }); 31 | System.out.println();*/ 32 | } 33 | 34 | static class TrainJourney { 35 | public int price; 36 | public TrainJourney onward; 37 | 38 | public TrainJourney(int p, TrainJourney t) { 39 | price = p; 40 | onward = t; 41 | } 42 | } 43 | 44 | static TrainJourney link(TrainJourney a, TrainJourney b) { 45 | if (a == null) { 46 | return b; 47 | } 48 | TrainJourney t = a; 49 | while (t.onward != null) { 50 | t = t.onward; 51 | } 52 | t.onward = b; 53 | return a; 54 | } 55 | 56 | static TrainJourney append(TrainJourney a, TrainJourney b) { 57 | return a == null ? b : new TrainJourney(a.price, append(a.onward, b)); 58 | } 59 | 60 | static void visit(TrainJourney journey, Consumer c) { 61 | if (journey != null) { 62 | c.accept(journey); 63 | visit(journey.onward, c); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap14/PersistentTree.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap14; 2 | 3 | public class PersistentTree { 4 | 5 | public static void main(String[] args) { 6 | Tree t = new Tree("Mary", 22, 7 | new Tree("Emily", 20, 8 | new Tree("Alan", 50, null, null), 9 | new Tree("Georgie", 23, null, null) 10 | ), 11 | new Tree("Tian", 29, 12 | new Tree("Raoul", 23, null, null), 13 | null 14 | ) 15 | ); 16 | 17 | // found = 23 18 | System.out.println(lookup("Raoul", -1, t)); 19 | // not found = -1 20 | System.out.println(lookup("Jeff", -1, t)); 21 | 22 | Tree f = fupdate("Jeff", 80, t); 23 | // found = 80 24 | System.out.println(lookup("Jeff", -1, f)); 25 | 26 | Tree u = update("Jim", 40, t); 27 | // t was not altered by fupdate, so Jeff is not found = -1 28 | System.out.println(lookup("Jeff", -1, u)); 29 | // found = 40 30 | System.out.println(lookup("Jim", -1, u)); 31 | 32 | Tree f2 = fupdate("Jeff", 80, t); 33 | // found = 80 34 | System.out.println(lookup("Jeff", -1, f2)); 35 | // f2 built from t altered by update() above, so Jim is still present = 40 36 | System.out.println(lookup("Jim", -1, f2)); 37 | } 38 | 39 | static class Tree { 40 | private String key; 41 | private int val; 42 | private Tree left, right; 43 | 44 | public Tree(String k, int v, Tree l, Tree r) { 45 | key = k; 46 | val = v; 47 | left = l; 48 | right = r; 49 | } 50 | } 51 | 52 | public static int lookup(String k, int defaultval, Tree t) { 53 | if (t == null) 54 | return defaultval; 55 | if (k.equals(t.key)) 56 | return t.val; 57 | return lookup(k, defaultval, k.compareTo(t.key) < 0 ? t.left : t.right); 58 | } 59 | 60 | public static Tree update(String k, int newval, Tree t) { 61 | if (t == null) 62 | t = new Tree(k, newval, null, null); 63 | else if (k.equals(t.key)) 64 | t.val = newval; 65 | else if (k.compareTo(t.key) < 0) 66 | t.left = update(k, newval, t.left); 67 | else 68 | t.right = update(k, newval, t.right); 69 | return t; 70 | } 71 | 72 | public static Tree fupdate(String k, int newval, Tree t) { 73 | return (t == null) ? 74 | new Tree(k, newval, null, null) : 75 | k.equals(t.key) ? 76 | new Tree(k, newval, t.left, t.right) : 77 | k.compareTo(t.key) < 0 ? 78 | new Tree(t.key, t.val, fupdate(k,newval, t.left), t.right) : 79 | new Tree(t.key, t.val, t.left, fupdate(k,newval, t.right)); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap2/FilteringApples.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap2; 2 | 3 | import java.util.*; 4 | 5 | public class FilteringApples{ 6 | 7 | public static void main(String ... args){ 8 | 9 | List inventory = Arrays.asList(new Apple(80,"green"), new Apple(155, "green"), new Apple(120, "red")); 10 | 11 | // [Apple{color='green', weight=80}, Apple{color='green', weight=155}] 12 | List greenApples = filterApplesByColor(inventory, "green"); 13 | System.out.println(greenApples); 14 | 15 | // [Apple{color='red', weight=120}] 16 | List redApples = filterApplesByColor(inventory, "red"); 17 | System.out.println(redApples); 18 | 19 | // [Apple{color='green', weight=80}, Apple{color='green', weight=155}] 20 | List greenApples2 = filter(inventory, new AppleColorPredicate()); 21 | System.out.println(greenApples2); 22 | 23 | // [Apple{color='green', weight=155}] 24 | List heavyApples = filter(inventory, new AppleWeightPredicate()); 25 | System.out.println(heavyApples); 26 | 27 | // [] 28 | List redAndHeavyApples = filter(inventory, new AppleRedAndHeavyPredicate()); 29 | System.out.println(redAndHeavyApples); 30 | 31 | // [Apple{color='red', weight=120}] 32 | List redApples2 = filter(inventory, new ApplePredicate() { 33 | public boolean test(Apple a){ 34 | return a.getColor().equals("red"); 35 | } 36 | }); 37 | System.out.println(redApples2); 38 | 39 | } 40 | 41 | public static List filterGreenApples(List inventory){ 42 | List result = new ArrayList<>(); 43 | for(Apple apple: inventory){ 44 | if("green".equals(apple.getColor())){ 45 | result.add(apple); 46 | } 47 | } 48 | return result; 49 | } 50 | 51 | public static List filterApplesByColor(List inventory, String color){ 52 | List result = new ArrayList<>(); 53 | for(Apple apple: inventory){ 54 | if(apple.getColor().equals(color)){ 55 | result.add(apple); 56 | } 57 | } 58 | return result; 59 | } 60 | 61 | public static List filterApplesByWeight(List inventory, int weight){ 62 | List result = new ArrayList<>(); 63 | for(Apple apple: inventory){ 64 | if(apple.getWeight() > weight){ 65 | result.add(apple); 66 | } 67 | } 68 | return result; 69 | } 70 | 71 | 72 | public static List filter(List inventory, ApplePredicate p){ 73 | List result = new ArrayList<>(); 74 | for(Apple apple : inventory){ 75 | if(p.test(apple)){ 76 | result.add(apple); 77 | } 78 | } 79 | return result; 80 | } 81 | 82 | public static class Apple { 83 | private int weight = 0; 84 | private String color = ""; 85 | 86 | public Apple(int weight, String color){ 87 | this.weight = weight; 88 | this.color = color; 89 | } 90 | 91 | public Integer getWeight() { 92 | return weight; 93 | } 94 | 95 | public void setWeight(Integer weight) { 96 | this.weight = weight; 97 | } 98 | 99 | public String getColor() { 100 | return color; 101 | } 102 | 103 | public void setColor(String color) { 104 | this.color = color; 105 | } 106 | 107 | public String toString() { 108 | return "Apple{" + 109 | "color='" + color + '\'' + 110 | ", weight=" + weight + 111 | '}'; 112 | } 113 | } 114 | 115 | interface ApplePredicate{ 116 | public boolean test(Apple a); 117 | } 118 | 119 | static class AppleWeightPredicate implements ApplePredicate{ 120 | public boolean test(Apple apple){ 121 | return apple.getWeight() > 150; 122 | } 123 | } 124 | static class AppleColorPredicate implements ApplePredicate{ 125 | public boolean test(Apple apple){ 126 | return "green".equals(apple.getColor()); 127 | } 128 | } 129 | 130 | static class AppleRedAndHeavyPredicate implements ApplePredicate{ 131 | public boolean test(Apple apple){ 132 | return "red".equals(apple.getColor()) 133 | && apple.getWeight() > 150; 134 | } 135 | } 136 | } -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap2/MeaningOfThis.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap2; 2 | 3 | public class MeaningOfThis 4 | { 5 | public final int value = 4; 6 | public void doIt() 7 | { 8 | int value = 6; 9 | Runnable r = new Runnable(){ 10 | public final int value = 5; 11 | public void run(){ 12 | int value = 10; 13 | System.out.println(this.value); 14 | } 15 | }; 16 | r.run(); 17 | } 18 | public static void main(String...args) 19 | { 20 | MeaningOfThis m = new MeaningOfThis(); 21 | m.doIt(); // ??? 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap3/ExecuteAround.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap3; 2 | 3 | import java.io.*; 4 | public class ExecuteAround { 5 | 6 | public static void main(String ...args) throws IOException{ 7 | 8 | // method we want to refactor to make more flexible 9 | String result = processFileLimited(); 10 | System.out.println(result); 11 | 12 | System.out.println("---"); 13 | 14 | String oneLine = processFile((BufferedReader b) -> b.readLine()); 15 | System.out.println(oneLine); 16 | 17 | String twoLines = processFile((BufferedReader b) -> b.readLine() + b.readLine()); 18 | System.out.println(twoLines); 19 | 20 | } 21 | 22 | public static String processFileLimited() throws IOException { 23 | try (BufferedReader br = 24 | new BufferedReader(new FileReader("lambdasinaction/chap3/data.txt"))) { 25 | return br.readLine(); 26 | } 27 | } 28 | 29 | 30 | public static String processFile(BufferedReaderProcessor p) throws IOException { 31 | try(BufferedReader br = new BufferedReader(new FileReader("lambdasinaction/chap3/data.txt"))){ 32 | return p.process(br); 33 | } 34 | 35 | } 36 | 37 | public interface BufferedReaderProcessor{ 38 | public String process(BufferedReader b) throws IOException; 39 | 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap3/Lambdas.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap3; 2 | 3 | import java.util.*; 4 | 5 | public class Lambdas { 6 | public static void main(String ...args){ 7 | 8 | // Simple example 9 | Runnable r = () -> System.out.println("Hello!"); 10 | r.run(); 11 | 12 | // Filtering with lambdas 13 | List inventory = Arrays.asList(new Apple(80,"green"), new Apple(155, "green"), new Apple(120, "red")); 14 | 15 | // [Apple{color='green', weight=80}, Apple{color='green', weight=155}] 16 | List greenApples = filter(inventory, (Apple a) -> "green".equals(a.getColor())); 17 | System.out.println(greenApples); 18 | 19 | 20 | Comparator c = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()); 21 | 22 | // [Apple{color='green', weight=80}, Apple{color='red', weight=120}, Apple{color='green', weight=155}] 23 | inventory.sort(c); 24 | System.out.println(inventory); 25 | } 26 | 27 | public static List filter(List inventory, ApplePredicate p){ 28 | List result = new ArrayList<>(); 29 | for(Apple apple : inventory){ 30 | if(p.test(apple)){ 31 | result.add(apple); 32 | } 33 | } 34 | return result; 35 | } 36 | 37 | public static class Apple { 38 | private int weight = 0; 39 | private String color = ""; 40 | 41 | public Apple(int weight, String color){ 42 | this.weight = weight; 43 | this.color = color; 44 | } 45 | 46 | public Integer getWeight() { 47 | return weight; 48 | } 49 | 50 | public void setWeight(Integer weight) { 51 | this.weight = weight; 52 | } 53 | 54 | public String getColor() { 55 | return color; 56 | } 57 | 58 | public void setColor(String color) { 59 | this.color = color; 60 | } 61 | 62 | public String toString() { 63 | return "Apple{" + 64 | "color='" + color + '\'' + 65 | ", weight=" + weight + 66 | '}'; 67 | } 68 | } 69 | 70 | interface ApplePredicate{ 71 | public boolean test(Apple a); 72 | } 73 | } -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap3/Sorting.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap3; 2 | 3 | import java.util.*; 4 | import static java.util.Comparator.comparing; 5 | 6 | public class Sorting { 7 | 8 | public static void main(String...args){ 9 | 10 | // 1 11 | List inventory = new ArrayList<>(); 12 | inventory.addAll(Arrays.asList(new Apple(80,"green"), new Apple(155, "green"), new Apple(120, "red"))); 13 | 14 | // [Apple{color='green', weight=80}, Apple{color='red', weight=120}, Apple{color='green', weight=155}] 15 | inventory.sort(new AppleComparator()); 16 | System.out.println(inventory); 17 | 18 | // reshuffling things a little 19 | inventory.set(1, new Apple(30, "green")); 20 | 21 | // 2 22 | // [Apple{color='green', weight=30}, Apple{color='green', weight=80}, Apple{color='green', weight=155}] 23 | inventory.sort(new Comparator() { 24 | public int compare(Apple a1, Apple a2){ 25 | return a1.getWeight().compareTo(a2.getWeight()); 26 | }}); 27 | System.out.println(inventory); 28 | 29 | // reshuffling things a little 30 | inventory.set(1, new Apple(20, "red")); 31 | 32 | // 3 33 | // [Apple{color='red', weight=20}, Apple{color='green', weight=30}, Apple{color='green', weight=155}] 34 | inventory.sort((a1, a2) -> a1.getWeight().compareTo(a2.getWeight())); 35 | System.out.println(inventory); 36 | 37 | // reshuffling things a little 38 | inventory.set(1, new Apple(10, "red")); 39 | 40 | // 4 41 | // [Apple{color='red', weight=10}, Apple{color='red', weight=20}, Apple{color='green', weight=155}] 42 | inventory.sort(comparing(Apple::getWeight)); 43 | System.out.println(inventory); 44 | } 45 | 46 | public static class Apple { 47 | private Integer weight = 0; 48 | private String color = ""; 49 | 50 | public Apple(Integer weight, String color){ 51 | this.weight = weight; 52 | this.color = color; 53 | } 54 | 55 | public Integer getWeight() { 56 | return weight; 57 | } 58 | 59 | public void setWeight(Integer weight) { 60 | this.weight = weight; 61 | } 62 | 63 | public String getColor() { 64 | return color; 65 | } 66 | 67 | public void setColor(String color) { 68 | this.color = color; 69 | } 70 | 71 | public String toString() { 72 | return "Apple{" + 73 | "color='" + color + '\'' + 74 | ", weight=" + weight + 75 | '}'; 76 | } 77 | } 78 | 79 | static class AppleComparator implements Comparator { 80 | public int compare(Apple a1, Apple a2){ 81 | return a1.getWeight().compareTo(a2.getWeight()); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap4/Dish.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap4; 2 | import java.util.*; 3 | 4 | public class Dish { 5 | 6 | private final String name; 7 | private final boolean vegetarian; 8 | private final int calories; 9 | private final Type type; 10 | 11 | public Dish(String name, boolean vegetarian, int calories, Type type) { 12 | this.name = name; 13 | this.vegetarian = vegetarian; 14 | this.calories = calories; 15 | this.type = type; 16 | } 17 | 18 | public String getName() { 19 | return name; 20 | } 21 | 22 | public boolean isVegetarian() { 23 | return vegetarian; 24 | } 25 | 26 | public int getCalories() { 27 | return calories; 28 | } 29 | 30 | public Type getType() { 31 | return type; 32 | } 33 | 34 | public enum Type { MEAT, FISH, OTHER } 35 | 36 | @Override 37 | public String toString() { 38 | return name; 39 | } 40 | 41 | public static final List menu = 42 | Arrays.asList( new Dish("pork", false, 800, Dish.Type.MEAT), 43 | new Dish("beef", false, 700, Dish.Type.MEAT), 44 | new Dish("chicken", false, 400, Dish.Type.MEAT), 45 | new Dish("french fries", true, 530, Dish.Type.OTHER), 46 | new Dish("rice", true, 350, Dish.Type.OTHER), 47 | new Dish("season fruit", true, 120, Dish.Type.OTHER), 48 | new Dish("pizza", true, 550, Dish.Type.OTHER), 49 | new Dish("prawns", false, 400, Dish.Type.FISH), 50 | new Dish("salmon", false, 450, Dish.Type.FISH)); 51 | } -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap4/StreamBasic.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap4; 2 | 3 | import java.util.*; 4 | import java.util.stream.*; 5 | 6 | import static java.util.Comparator.comparing; 7 | import static java.util.stream.Collectors.toList; 8 | 9 | import static lambdasinaction.chap4.Dish.menu; 10 | 11 | public class StreamBasic { 12 | 13 | public static void main(String...args){ 14 | // Java 7 15 | getLowCaloricDishesNamesInJava7(Dish.menu).forEach(System.out::println); 16 | 17 | System.out.println("---"); 18 | 19 | // Java 8 20 | getLowCaloricDishesNamesInJava8(Dish.menu).forEach(System.out::println); 21 | 22 | } 23 | 24 | public static List getLowCaloricDishesNamesInJava7(List dishes){ 25 | List lowCaloricDishes = new ArrayList<>(); 26 | for(Dish d: dishes){ 27 | if(d.getCalories() < 400){ 28 | lowCaloricDishes.add(d); 29 | } 30 | } 31 | List lowCaloricDishesName = new ArrayList<>(); 32 | Collections.sort(lowCaloricDishes, new Comparator() { 33 | public int compare(Dish d1, Dish d2){ 34 | return Integer.compare(d1.getCalories(), d2.getCalories()); 35 | } 36 | }); 37 | for(Dish d: lowCaloricDishes){ 38 | lowCaloricDishesName.add(d.getName()); 39 | } 40 | return lowCaloricDishesName; 41 | } 42 | 43 | public static List getLowCaloricDishesNamesInJava8(List dishes){ 44 | return dishes.stream() 45 | .filter(d -> d.getCalories() < 400) 46 | .sorted(comparing(Dish::getCalories)) 47 | .map(Dish::getName) 48 | .collect(toList()); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap4/StreamVsCollection.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap4; 2 | 3 | import java.util.*; 4 | import java.util.stream.*; 5 | import static java.util.stream.Collectors.toList; 6 | 7 | 8 | public class StreamVsCollection { 9 | 10 | public static void main(String...args){ 11 | List names = Arrays.asList("Java8", "Lambdas", "In", "Action"); 12 | Stream s = names.stream(); 13 | s.forEach(System.out::println); 14 | // uncommenting this line will result in an IllegalStateException 15 | // because streams can be consumed only once 16 | //s.forEach(System.out::println); 17 | } 18 | } -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap5/BuildingStreams.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap5; 2 | 3 | import java.util.*; 4 | import java.util.function.IntSupplier; 5 | import java.util.stream.*; 6 | import java.nio.charset.Charset; 7 | import java.nio.file.*; 8 | 9 | public class BuildingStreams { 10 | 11 | public static void main(String...args) throws Exception{ 12 | 13 | // Stream.of 14 | Stream stream = Stream.of("Java 8", "Lambdas", "In", "Action"); 15 | stream.map(String::toUpperCase).forEach(System.out::println); 16 | 17 | // Stream.empty 18 | Stream emptyStream = Stream.empty(); 19 | 20 | // Arrays.stream 21 | int[] numbers = {2, 3, 5, 7, 11, 13}; 22 | System.out.println(Arrays.stream(numbers).sum()); 23 | 24 | // Stream.iterate 25 | Stream.iterate(0, n -> n + 2) 26 | .limit(10) 27 | .forEach(System.out::println); 28 | 29 | // fibonnaci with iterate 30 | Stream.iterate(new int[]{0, 1}, t -> new int[]{t[1],t[0] + t[1]}) 31 | .limit(10) 32 | .forEach(t -> System.out.println("(" + t[0] + ", " + t[1] + ")")); 33 | 34 | Stream.iterate(new int[]{0, 1}, t -> new int[]{t[1],t[0] + t[1]}) 35 | .limit(10) 36 | . map(t -> t[0]) 37 | .forEach(System.out::println); 38 | 39 | // random stream of doubles with Stream.generate 40 | Stream.generate(Math::random) 41 | .limit(10) 42 | .forEach(System.out::println); 43 | 44 | // stream of 1s with Stream.generate 45 | IntStream.generate(() -> 1) 46 | .limit(5) 47 | .forEach(System.out::println); 48 | 49 | IntStream.generate(new IntSupplier(){ 50 | public int getAsInt(){ 51 | return 2; 52 | } 53 | }).limit(5) 54 | .forEach(System.out::println); 55 | 56 | 57 | IntSupplier fib = new IntSupplier(){ 58 | private int previous = 0; 59 | private int current = 1; 60 | public int getAsInt(){ 61 | int nextValue = this.previous + this.current; 62 | this.previous = this.current; 63 | this.current = nextValue; 64 | return this.previous; 65 | } 66 | }; 67 | IntStream.generate(fib).limit(10).forEach(System.out::println); 68 | 69 | long uniqueWords = Files.lines(Paths.get("lambdasinaction/chap5/data.txt"), Charset.defaultCharset()) 70 | .flatMap(line -> Arrays.stream(line.split(" "))) 71 | .distinct() 72 | .count(); 73 | 74 | System.out.println("There are " + uniqueWords + " unique words in data.txt"); 75 | 76 | 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap5/Filtering.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap5; 2 | import lambdasinaction.chap4.*; 3 | 4 | import java.util.stream.*; 5 | import java.util.*; 6 | import static java.util.stream.Collectors.toList; 7 | 8 | import static lambdasinaction.chap4.Dish.menu; 9 | 10 | public class Filtering{ 11 | 12 | public static void main(String...args){ 13 | 14 | // Filtering with predicate 15 | List vegetarianMenu = 16 | menu.stream() 17 | .filter(Dish::isVegetarian) 18 | .collect(toList()); 19 | 20 | vegetarianMenu.forEach(System.out::println); 21 | 22 | // Filtering unique elements 23 | List numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4); 24 | numbers.stream() 25 | .filter(i -> i % 2 == 0) 26 | .distinct() 27 | .forEach(System.out::println); 28 | 29 | // Truncating a stream 30 | List dishesLimit3 = 31 | menu.stream() 32 | .filter(d -> d.getCalories() > 300) 33 | .limit(3) 34 | .collect(toList()); 35 | 36 | dishesLimit3.forEach(System.out::println); 37 | 38 | // Skipping elements 39 | List dishesSkip2 = 40 | menu.stream() 41 | .filter(d -> d.getCalories() > 300) 42 | .skip(2) 43 | .collect(toList()); 44 | 45 | dishesSkip2.forEach(System.out::println); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap5/Finding.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap5; 2 | import lambdasinaction.chap4.*; 3 | 4 | import java.util.stream.*; 5 | import java.util.*; 6 | 7 | import static lambdasinaction.chap4.Dish.menu; 8 | 9 | public class Finding{ 10 | 11 | public static void main(String...args){ 12 | if(isVegetarianFriendlyMenu()){ 13 | System.out.println("Vegetarian friendly"); 14 | } 15 | 16 | System.out.println(isHealthyMenu()); 17 | System.out.println(isHealthyMenu2()); 18 | 19 | Optional dish = findVegetarianDish(); 20 | dish.ifPresent(d -> System.out.println(d.getName())); 21 | } 22 | 23 | private static boolean isVegetarianFriendlyMenu(){ 24 | return menu.stream().anyMatch(Dish::isVegetarian); 25 | } 26 | 27 | private static boolean isHealthyMenu(){ 28 | return menu.stream().allMatch(d -> d.getCalories() < 1000); 29 | } 30 | 31 | private static boolean isHealthyMenu2(){ 32 | return menu.stream().noneMatch(d -> d.getCalories() >= 1000); 33 | } 34 | 35 | private static Optional findVegetarianDish(){ 36 | return menu.stream().filter(Dish::isVegetarian).findAny(); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap5/Laziness.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap5; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | import static java.util.stream.Collectors.toList; 7 | 8 | /** 9 | * Created by raoul-gabrielurma on 14/01/2014. 10 | */ 11 | public class Laziness { 12 | 13 | public static void main(String[] args) { 14 | List numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8); 15 | List twoEvenSquares = 16 | numbers.stream() 17 | .filter(n -> { 18 | System.out.println("filtering " + n); return n % 2 == 0; 19 | }) 20 | .map(n -> { 21 | System.out.println("mapping " + n); 22 | return n * n; 23 | }) 24 | .limit(2) 25 | .collect(toList()); 26 | 27 | } 28 | 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap5/Mapping.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap5; 2 | 3 | import lambdasinaction.chap4.*; 4 | 5 | import java.util.*; 6 | import static java.util.stream.Collectors.toList; 7 | import static lambdasinaction.chap4.Dish.menu; 8 | 9 | public class Mapping{ 10 | 11 | public static void main(String...args){ 12 | 13 | // map 14 | List dishNames = menu.stream() 15 | .map(Dish::getName) 16 | .collect(toList()); 17 | System.out.println(dishNames); 18 | 19 | // map 20 | List words = Arrays.asList("Hello", "World"); 21 | List wordLengths = words.stream() 22 | .map(String::length) 23 | .collect(toList()); 24 | System.out.println(wordLengths); 25 | 26 | // flatMap 27 | words.stream() 28 | .flatMap((String line) -> Arrays.stream(line.split(""))) 29 | .distinct() 30 | .forEach(System.out::println); 31 | 32 | // flatMap 33 | List numbers1 = Arrays.asList(1,2,3,4,5); 34 | List numbers2 = Arrays.asList(6,7,8); 35 | List pairs = 36 | numbers1.stream() 37 | .flatMap((Integer i) -> numbers2.stream() 38 | .map((Integer j) -> new int[]{i, j}) 39 | ) 40 | .filter(pair -> (pair[0] + pair[1]) % 3 == 0) 41 | .collect(toList()); 42 | pairs.forEach(pair -> System.out.println("(" + pair[0] + ", " + pair[1] + ")")); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap5/NumericStreams.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap5; 2 | import lambdasinaction.chap4.*; 3 | 4 | import java.util.stream.*; 5 | import java.util.*; 6 | 7 | import static lambdasinaction.chap4.Dish.menu; 8 | 9 | public class NumericStreams{ 10 | 11 | public static void main(String...args){ 12 | 13 | List numbers = Arrays.asList(3,4,5,1,2); 14 | 15 | Arrays.stream(numbers.toArray()).forEach(System.out::println); 16 | int calories = menu.stream() 17 | .mapToInt(Dish::getCalories) 18 | .sum(); 19 | System.out.println("Number of calories:" + calories); 20 | 21 | 22 | // max and OptionalInt 23 | OptionalInt maxCalories = menu.stream() 24 | .mapToInt(Dish::getCalories) 25 | .max(); 26 | 27 | int max; 28 | if(maxCalories.isPresent()){ 29 | max = maxCalories.getAsInt(); 30 | } 31 | else { 32 | // we can choose a default value 33 | max = 1; 34 | } 35 | System.out.println(max); 36 | 37 | // numeric ranges 38 | IntStream evenNumbers = IntStream.rangeClosed(1, 100) 39 | .filter(n -> n % 2 == 0); 40 | 41 | System.out.println(evenNumbers.count()); 42 | 43 | Stream pythagoreanTriples = 44 | IntStream.rangeClosed(1, 100).boxed() 45 | .flatMap(a -> IntStream.rangeClosed(a, 100) 46 | .filter(b -> Math.sqrt(a*a + b*b) % 1 == 0).boxed() 47 | .map(b -> new int[]{a, b, (int) Math.sqrt(a * a + b * b)})); 48 | 49 | pythagoreanTriples.forEach(t -> System.out.println(t[0] + ", " + t[1] + ", " + t[2])); 50 | 51 | } 52 | 53 | public static boolean isPerfectSquare(int n){ 54 | return Math.sqrt(n) % 1 == 0; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap5/PuttingIntoPractice.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap5; 2 | 3 | import lambdasinaction.chap5.*; 4 | 5 | import java.util.*; 6 | 7 | import static java.util.Comparator.comparing; 8 | import static java.util.stream.Collectors.toList; 9 | 10 | public class PuttingIntoPractice{ 11 | public static void main(String ...args){ 12 | Trader raoul = new Trader("Raoul", "Cambridge"); 13 | Trader mario = new Trader("Mario","Milan"); 14 | Trader alan = new Trader("Alan","Cambridge"); 15 | Trader brian = new Trader("Brian","Cambridge"); 16 | 17 | List transactions = Arrays.asList( 18 | new Transaction(brian, 2011, 300), 19 | new Transaction(raoul, 2012, 1000), 20 | new Transaction(raoul, 2011, 400), 21 | new Transaction(mario, 2012, 710), 22 | new Transaction(mario, 2012, 700), 23 | new Transaction(alan, 2012, 950) 24 | ); 25 | 26 | 27 | // Query 1: Find all transactions from year 2011 and sort them by value (small to high). 28 | List tr2011 = transactions.stream() 29 | .filter(transaction -> transaction.getYear() == 2011) 30 | .sorted(comparing(Transaction::getValue)) 31 | .collect(toList()); 32 | System.out.println(tr2011); 33 | 34 | // Query 2: What are all the unique cities where the traders work? 35 | List cities = 36 | transactions.stream() 37 | .map(transaction -> transaction.getTrader().getCity()) 38 | .distinct() 39 | .collect(toList()); 40 | System.out.println(cities); 41 | 42 | // Query 3: Find all traders from Cambridge and sort them by name. 43 | 44 | List traders = 45 | transactions.stream() 46 | .map(Transaction::getTrader) 47 | .filter(trader -> trader.getCity().equals("Cambridge")) 48 | .distinct() 49 | .sorted(comparing(Trader::getName)) 50 | .collect(toList()); 51 | System.out.println(traders); 52 | 53 | 54 | // Query 4: Return a string of all traders’ names sorted alphabetically. 55 | 56 | String traderStr = 57 | transactions.stream() 58 | .map(transaction -> transaction.getTrader().getName()) 59 | .distinct() 60 | .sorted() 61 | .reduce("", (n1, n2) -> n1 + n2); 62 | System.out.println(traderStr); 63 | 64 | // Query 5: Are there any trader based in Milan? 65 | 66 | boolean milanBased = 67 | transactions.stream() 68 | .anyMatch(transaction -> transaction.getTrader() 69 | .getCity() 70 | .equals("Milan") 71 | ); 72 | System.out.println(milanBased); 73 | 74 | 75 | // Query 6: Update all transactions so that the traders from Milan are set to Cambridge. 76 | transactions.stream() 77 | .map(Transaction::getTrader) 78 | .filter(trader -> trader.getCity().equals("Milan")) 79 | .forEach(trader -> trader.setCity("Cambridge")); 80 | System.out.println(transactions); 81 | 82 | 83 | // Query 7: What's the highest value in all the transactions? 84 | int highestValue = 85 | transactions.stream() 86 | .map(Transaction::getValue) 87 | .reduce(0, Integer::max); 88 | System.out.println(highestValue); 89 | } 90 | } -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap5/Reducing.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap5; 2 | import lambdasinaction.chap4.*; 3 | 4 | import java.util.stream.*; 5 | import java.util.*; 6 | 7 | import static lambdasinaction.chap4.Dish.menu; 8 | 9 | public class Reducing{ 10 | 11 | public static void main(String...args){ 12 | 13 | List numbers = Arrays.asList(3,4,5,1,2); 14 | int sum = numbers.stream().reduce(0, (a, b) -> a + b); 15 | System.out.println(sum); 16 | 17 | int sum2 = numbers.stream().reduce(0, Integer::sum); 18 | System.out.println(sum2); 19 | 20 | int max = numbers.stream().reduce(0, (a, b) -> Integer.max(a, b)); 21 | System.out.println(max); 22 | 23 | Optional min = numbers.stream().reduce(Integer::min); 24 | min.ifPresent(System.out::println); 25 | 26 | int calories = menu.stream() 27 | .map(Dish::getCalories) 28 | .reduce(0, Integer::sum); 29 | System.out.println("Number of calories:" + calories); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap5/Trader.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap5; 2 | public class Trader{ 3 | 4 | private String name; 5 | private String city; 6 | 7 | public Trader(String n, String c){ 8 | this.name = n; 9 | this.city = c; 10 | } 11 | 12 | public String getName(){ 13 | return this.name; 14 | } 15 | 16 | public String getCity(){ 17 | return this.city; 18 | } 19 | 20 | public void setCity(String newCity){ 21 | this.city = newCity; 22 | } 23 | 24 | public String toString(){ 25 | return "Trader:"+this.name + " in " + this.city; 26 | } 27 | } -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap5/Transaction.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap5; 2 | 3 | public class Transaction{ 4 | 5 | private Trader trader; 6 | private int year; 7 | private int value; 8 | 9 | public Transaction(Trader trader, int year, int value) 10 | { 11 | this.trader = trader; 12 | this.year = year; 13 | this.value = value; 14 | } 15 | 16 | public Trader getTrader(){ 17 | return this.trader; 18 | } 19 | 20 | public int getYear(){ 21 | return this.year; 22 | } 23 | 24 | public int getValue(){ 25 | return this.value; 26 | } 27 | 28 | public String toString(){ 29 | return "{" + this.trader + ", " + 30 | "year: "+this.year+", " + 31 | "value:" + this.value +"}"; 32 | } 33 | } -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap6/CollectorHarness.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap6; 2 | 3 | import java.util.function.*; 4 | 5 | public class CollectorHarness { 6 | 7 | public static void main(String[] args) { 8 | //System.out.println("Partitioning done in: " + execute(PartitionPrimeNumbers::partitionPrimes) + " msecs"); 9 | System.out.println("Partitioning done in: " + execute(PartitionPrimeNumbers::partitionPrimesWithCustomCollector) + " msecs" ); 10 | } 11 | 12 | private static long execute(Consumer primePartitioner) { 13 | long fastest = Long.MAX_VALUE; 14 | for (int i = 0; i < 10; i++) { 15 | long start = System.nanoTime(); 16 | primePartitioner.accept(1_000_000); 17 | long duration = (System.nanoTime() - start) / 1_000_000; 18 | if (duration < fastest) fastest = duration; 19 | System.out.println("done in " + duration); 20 | } 21 | return fastest; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap6/Dish.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap6; 2 | 3 | import java.util.*; 4 | 5 | import static java.util.Arrays.asList; 6 | 7 | public class Dish { 8 | 9 | private final String name; 10 | private final boolean vegetarian; 11 | private final int calories; 12 | private final Type type; 13 | 14 | public Dish(String name, boolean vegetarian, int calories, Type type) { 15 | this.name = name; 16 | this.vegetarian = vegetarian; 17 | this.calories = calories; 18 | this.type = type; 19 | } 20 | 21 | public String getName() { 22 | return name; 23 | } 24 | 25 | public boolean isVegetarian() { 26 | return vegetarian; 27 | } 28 | 29 | public int getCalories() { 30 | return calories; 31 | } 32 | 33 | public Type getType() { 34 | return type; 35 | } 36 | 37 | public enum Type { MEAT, FISH, OTHER } 38 | 39 | @Override 40 | public String toString() { 41 | return name; 42 | } 43 | 44 | public static final List menu = 45 | asList( new Dish("pork", false, 800, Dish.Type.MEAT), 46 | new Dish("beef", false, 700, Dish.Type.MEAT), 47 | new Dish("chicken", false, 400, Dish.Type.MEAT), 48 | new Dish("french fries", true, 530, Dish.Type.OTHER), 49 | new Dish("rice", true, 350, Dish.Type.OTHER), 50 | new Dish("season fruit", true, 120, Dish.Type.OTHER), 51 | new Dish("pizza", true, 550, Dish.Type.OTHER), 52 | new Dish("prawns", false, 400, Dish.Type.FISH), 53 | new Dish("salmon", false, 450, Dish.Type.FISH)); 54 | 55 | public static final Map> dishTags = new HashMap<>(); 56 | 57 | static { 58 | dishTags.put("pork", asList("greasy", "salty")); 59 | dishTags.put("beef", asList("salty", "roasted")); 60 | dishTags.put("chicken", asList("fried", "crisp")); 61 | dishTags.put("french fries", asList("greasy", "fried")); 62 | dishTags.put("rice", asList("light", "natural")); 63 | dishTags.put("season fruit", asList("fresh", "natural")); 64 | dishTags.put("pizza", asList("tasty", "salty")); 65 | dishTags.put("prawns", asList("tasty", "roasted")); 66 | dishTags.put("salmon", asList("delicious", "fresh")); 67 | } 68 | } -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap6/Grouping.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap6; 2 | 3 | import java.util.*; 4 | 5 | import static java.util.stream.Collectors.*; 6 | import static lambdasinaction.chap6.Dish.dishTags; 7 | import static lambdasinaction.chap6.Dish.menu; 8 | 9 | public class Grouping { 10 | 11 | enum CaloricLevel { DIET, NORMAL, FAT }; 12 | 13 | public static void main(String ... args) { 14 | System.out.println("Dishes grouped by type: " + groupDishesByType()); 15 | System.out.println("Dish names grouped by type: " + groupDishNamesByType()); 16 | System.out.println("Dish tags grouped by type: " + groupDishTagsByType()); 17 | System.out.println("Caloric dishes grouped by type: " + groupCaloricDishesByType()); 18 | System.out.println("Dishes grouped by caloric level: " + groupDishesByCaloricLevel()); 19 | System.out.println("Dishes grouped by type and caloric level: " + groupDishedByTypeAndCaloricLevel()); 20 | System.out.println("Count dishes in groups: " + countDishesInGroups()); 21 | System.out.println("Most caloric dishes by type: " + mostCaloricDishesByType()); 22 | System.out.println("Most caloric dishes by type: " + mostCaloricDishesByTypeWithoutOprionals()); 23 | System.out.println("Sum calories by type: " + sumCaloriesByType()); 24 | System.out.println("Caloric levels by type: " + caloricLevelsByType()); 25 | } 26 | 27 | private static Map> groupDishesByType() { 28 | return menu.stream().collect(groupingBy(Dish::getType)); 29 | } 30 | 31 | private static Map> groupDishNamesByType() { 32 | return menu.stream().collect(groupingBy(Dish::getType, mapping(Dish::getName, toList()))); 33 | } 34 | 35 | private static Map> groupDishTagsByType() { 36 | return menu.stream().collect(groupingBy(Dish::getType, flatMapping(dish -> dishTags.get( dish.getName() ).stream(), toSet()))); 37 | } 38 | 39 | private static Map> groupCaloricDishesByType() { 40 | // return menu.stream().filter(dish -> dish.getCalories() > 500).collect(groupingBy(Dish::getType)); 41 | return menu.stream().collect(groupingBy(Dish::getType, filtering(dish -> dish.getCalories() > 500, toList()))); 42 | } 43 | 44 | private static Map> groupDishesByCaloricLevel() { 45 | return menu.stream().collect( 46 | groupingBy(dish -> { 47 | if (dish.getCalories() <= 400) return CaloricLevel.DIET; 48 | else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL; 49 | else return CaloricLevel.FAT; 50 | } )); 51 | } 52 | 53 | private static Map>> groupDishedByTypeAndCaloricLevel() { 54 | return menu.stream().collect( 55 | groupingBy(Dish::getType, 56 | groupingBy((Dish dish) -> { 57 | if (dish.getCalories() <= 400) return CaloricLevel.DIET; 58 | else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL; 59 | else return CaloricLevel.FAT; 60 | } ) 61 | ) 62 | ); 63 | } 64 | 65 | private static Map countDishesInGroups() { 66 | return menu.stream().collect(groupingBy(Dish::getType, counting())); 67 | } 68 | 69 | private static Map> mostCaloricDishesByType() { 70 | return menu.stream().collect( 71 | groupingBy(Dish::getType, 72 | reducing((Dish d1, Dish d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2))); 73 | } 74 | 75 | private static Map mostCaloricDishesByTypeWithoutOprionals() { 76 | return menu.stream().collect( 77 | groupingBy(Dish::getType, 78 | collectingAndThen( 79 | reducing((d1, d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2), 80 | Optional::get))); 81 | } 82 | 83 | private static Map sumCaloriesByType() { 84 | return menu.stream().collect(groupingBy(Dish::getType, 85 | summingInt(Dish::getCalories))); 86 | } 87 | 88 | private static Map> caloricLevelsByType() { 89 | return menu.stream().collect( 90 | groupingBy(Dish::getType, mapping( 91 | dish -> { if (dish.getCalories() <= 400) return CaloricLevel.DIET; 92 | else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL; 93 | else return CaloricLevel.FAT; }, 94 | toSet() ))); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap6/GroupingTransactions.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap6; 2 | 3 | import java.util.*; 4 | 5 | import static java.util.stream.Collectors.groupingBy; 6 | 7 | public class GroupingTransactions { 8 | 9 | public static List transactions = Arrays.asList( new Transaction(Currency.EUR, 1500.0), 10 | new Transaction(Currency.USD, 2300.0), 11 | new Transaction(Currency.GBP, 9900.0), 12 | new Transaction(Currency.EUR, 1100.0), 13 | new Transaction(Currency.JPY, 7800.0), 14 | new Transaction(Currency.CHF, 6700.0), 15 | new Transaction(Currency.EUR, 5600.0), 16 | new Transaction(Currency.USD, 4500.0), 17 | new Transaction(Currency.CHF, 3400.0), 18 | new Transaction(Currency.GBP, 3200.0), 19 | new Transaction(Currency.USD, 4600.0), 20 | new Transaction(Currency.JPY, 5700.0), 21 | new Transaction(Currency.EUR, 6800.0) ); 22 | public static void main(String ... args) { 23 | groupImperatively(); 24 | groupFunctionally(); 25 | 26 | } 27 | 28 | private static void groupImperatively() { 29 | Map> transactionsByCurrencies = new HashMap<>(); 30 | for (Transaction transaction : transactions) { 31 | Currency currency = transaction.getCurrency(); 32 | List transactionsForCurrency = transactionsByCurrencies.get(currency); 33 | if (transactionsForCurrency == null) { 34 | transactionsForCurrency = new ArrayList<>(); 35 | transactionsByCurrencies.put(currency, transactionsForCurrency); 36 | } 37 | transactionsForCurrency.add(transaction); 38 | } 39 | 40 | System.out.println(transactionsByCurrencies); 41 | } 42 | 43 | private static void groupFunctionally() { 44 | Map> transactionsByCurrencies = transactions.stream().collect(groupingBy(Transaction::getCurrency)); 45 | System.out.println(transactionsByCurrencies); 46 | } 47 | 48 | public static class Transaction { 49 | private final Currency currency; 50 | private final double value; 51 | 52 | public Transaction(Currency currency, double value) { 53 | this.currency = currency; 54 | this.value = value; 55 | } 56 | 57 | public Currency getCurrency() { 58 | return currency; 59 | } 60 | 61 | public double getValue() { 62 | return value; 63 | } 64 | 65 | @Override 66 | public String toString() { 67 | return currency + " " + value; 68 | } 69 | } 70 | 71 | public enum Currency { 72 | EUR, USD, JPY, GBP, CHF 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap6/PartitionPrimeNumbers.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap6; 2 | 3 | import java.util.*; 4 | import java.util.function.*; 5 | import java.util.stream.*; 6 | 7 | import static java.util.stream.Collectors.*; 8 | import static java.util.stream.Collector.Characteristics.*; 9 | 10 | public class PartitionPrimeNumbers { 11 | 12 | public static void main(String ... args) { 13 | System.out.println("Numbers partitioned in prime and non-prime: " + partitionPrimes(100)); 14 | System.out.println("Numbers partitioned in prime and non-prime: " + partitionPrimesWithCustomCollector(100)); 15 | 16 | } 17 | 18 | public static Map> partitionPrimes(int n) { 19 | return IntStream.rangeClosed(2, n).boxed() 20 | .collect(partitioningBy(candidate -> isPrime(candidate))); 21 | } 22 | 23 | public static boolean isPrime(int candidate) { 24 | return IntStream.rangeClosed(2, candidate-1) 25 | .limit((long) Math.floor(Math.sqrt((double) candidate)) - 1) 26 | .noneMatch(i -> candidate % i == 0); 27 | } 28 | 29 | public static Map> partitionPrimesWithCustomCollector(int n) { 30 | return IntStream.rangeClosed(2, n).boxed().collect(new PrimeNumbersCollector()); 31 | } 32 | 33 | public static boolean isPrime(List primes, Integer candidate) { 34 | double candidateRoot = Math.sqrt((double) candidate); 35 | //return takeWhile(primes, i -> i <= candidateRoot).stream().noneMatch(i -> candidate % i == 0); 36 | return primes.stream().takeWhile(i -> i <= candidateRoot).noneMatch(i -> candidate % i == 0); 37 | } 38 | /* 39 | public static List takeWhile(List list, Predicate p) { 40 | int i = 0; 41 | for (A item : list) { 42 | if (!p.test(item)) { 43 | return list.subList(0, i); 44 | } 45 | i++; 46 | } 47 | return list; 48 | } 49 | */ 50 | public static class PrimeNumbersCollector 51 | implements Collector>, Map>> { 52 | 53 | @Override 54 | public Supplier>> supplier() { 55 | return () -> new HashMap>() {{ 56 | put(true, new ArrayList()); 57 | put(false, new ArrayList()); 58 | }}; 59 | } 60 | 61 | @Override 62 | public BiConsumer>, Integer> accumulator() { 63 | return (Map> acc, Integer candidate) -> { 64 | acc.get( isPrime( acc.get(true), 65 | candidate) ) 66 | .add(candidate); 67 | }; 68 | } 69 | 70 | @Override 71 | public BinaryOperator>> combiner() { 72 | return (Map> map1, Map> map2) -> { 73 | map1.get(true).addAll(map2.get(true)); 74 | map1.get(false).addAll(map2.get(false)); 75 | return map1; 76 | }; 77 | } 78 | 79 | @Override 80 | public Function>, Map>> finisher() { 81 | return i -> i; 82 | } 83 | 84 | @Override 85 | public Set characteristics() { 86 | return Collections.unmodifiableSet(EnumSet.of(IDENTITY_FINISH)); 87 | } 88 | } 89 | 90 | public Map> partitionPrimesWithInlineCollector(int n) { 91 | return Stream.iterate(2, i -> i + 1).limit(n) 92 | .collect( 93 | () -> new HashMap>() {{ 94 | put(true, new ArrayList()); 95 | put(false, new ArrayList()); 96 | }}, 97 | (acc, candidate) -> { 98 | acc.get( isPrime(acc.get(true), candidate) ) 99 | .add(candidate); 100 | }, 101 | (map1, map2) -> { 102 | map1.get(true).addAll(map2.get(true)); 103 | map1.get(false).addAll(map2.get(false)); 104 | }); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap6/Partitioning.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap6; 2 | 3 | import java.util.*; 4 | 5 | import static java.util.Comparator.comparingInt; 6 | import static java.util.stream.Collectors.*; 7 | import static lambdasinaction.chap6.Dish.menu; 8 | 9 | public class Partitioning { 10 | 11 | public static void main(String ... args) { 12 | System.out.println("Dishes partitioned by vegetarian: " + partitionByVegeterian()); 13 | System.out.println("Vegetarian Dishes by type: " + vegetarianDishesByType()); 14 | System.out.println("Most caloric dishes by vegetarian: " + mostCaloricPartitionedByVegetarian()); 15 | } 16 | 17 | private static Map> partitionByVegeterian() { 18 | return menu.stream().collect(partitioningBy(Dish::isVegetarian)); 19 | } 20 | 21 | private static Map>> vegetarianDishesByType() { 22 | return menu.stream().collect(partitioningBy(Dish::isVegetarian, groupingBy(Dish::getType))); 23 | } 24 | 25 | private static Object mostCaloricPartitionedByVegetarian() { 26 | return menu.stream().collect( 27 | partitioningBy(Dish::isVegetarian, 28 | collectingAndThen( 29 | maxBy(comparingInt(Dish::getCalories)), 30 | Optional::get))); 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap6/Reducing.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap6; 2 | 3 | import static java.util.stream.Collectors.*; 4 | import static lambdasinaction.chap6.Dish.menu; 5 | 6 | public class Reducing { 7 | 8 | public static void main(String ... args) { 9 | System.out.println("Total calories in menu: " + calculateTotalCalories()); 10 | System.out.println("Total calories in menu: " + calculateTotalCaloriesWithMethodReference()); 11 | System.out.println("Total calories in menu: " + calculateTotalCaloriesWithoutCollectors()); 12 | System.out.println("Total calories in menu: " + calculateTotalCaloriesUsingSum()); 13 | } 14 | 15 | private static int calculateTotalCalories() { 16 | return menu.stream().collect(reducing(0, Dish::getCalories, (Integer i, Integer j) -> i + j)); 17 | } 18 | 19 | private static int calculateTotalCaloriesWithMethodReference() { 20 | return menu.stream().collect(reducing(0, Dish::getCalories, Integer::sum)); 21 | } 22 | 23 | private static int calculateTotalCaloriesWithoutCollectors() { 24 | return menu.stream().map(Dish::getCalories).reduce(Integer::sum).get(); 25 | } 26 | 27 | private static int calculateTotalCaloriesUsingSum() { 28 | return menu.stream().mapToInt(Dish::getCalories).sum(); 29 | } 30 | } -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap6/Summarizing.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap6; 2 | 3 | import java.util.*; 4 | import java.util.function.*; 5 | 6 | import static java.util.stream.Collectors.*; 7 | import static lambdasinaction.chap6.Dish.menu; 8 | 9 | public class Summarizing { 10 | 11 | public static void main(String ... args) { 12 | System.out.println("Nr. of dishes: " + howManyDishes()); 13 | System.out.println("The most caloric dish is: " + findMostCaloricDish()); 14 | System.out.println("The most caloric dish is: " + findMostCaloricDishUsingComparator()); 15 | System.out.println("Total calories in menu: " + calculateTotalCalories()); 16 | System.out.println("Average calories in menu: " + calculateAverageCalories()); 17 | System.out.println("Menu statistics: " + calculateMenuStatistics()); 18 | System.out.println("Short menu: " + getShortMenu()); 19 | System.out.println("Short menu comma separated: " + getShortMenuCommaSeparated()); 20 | } 21 | 22 | 23 | private static long howManyDishes() { 24 | return menu.stream().collect(counting()); 25 | } 26 | 27 | private static Dish findMostCaloricDish() { 28 | return menu.stream().collect(reducing((d1, d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2)).get(); 29 | } 30 | 31 | private static Dish findMostCaloricDishUsingComparator() { 32 | Comparator dishCaloriesComparator = Comparator.comparingInt(Dish::getCalories); 33 | BinaryOperator moreCaloricOf = BinaryOperator.maxBy(dishCaloriesComparator); 34 | return menu.stream().collect(reducing(moreCaloricOf)).get(); 35 | } 36 | 37 | private static int calculateTotalCalories() { 38 | return menu.stream().collect(summingInt(Dish::getCalories)); 39 | } 40 | 41 | private static Double calculateAverageCalories() { 42 | return menu.stream().collect(averagingInt(Dish::getCalories)); 43 | } 44 | 45 | private static IntSummaryStatistics calculateMenuStatistics() { 46 | return menu.stream().collect(summarizingInt(Dish::getCalories)); 47 | } 48 | 49 | private static String getShortMenu() { 50 | return menu.stream().map(Dish::getName).collect(joining()); 51 | } 52 | 53 | private static String getShortMenuCommaSeparated() { 54 | return menu.stream().map(Dish::getName).collect(joining(", ")); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap6/ToListCollector.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap6; 2 | 3 | import java.util.*; 4 | import java.util.function.*; 5 | import java.util.stream.Collector; 6 | import static java.util.stream.Collector.Characteristics.*; 7 | 8 | public class ToListCollector implements Collector, List> { 9 | 10 | @Override 11 | public Supplier> supplier() { 12 | return () -> new ArrayList(); 13 | } 14 | 15 | @Override 16 | public BiConsumer, T> accumulator() { 17 | return (list, item) -> list.add(item); 18 | } 19 | 20 | @Override 21 | public Function, List> finisher() { 22 | return i -> i; 23 | } 24 | 25 | @Override 26 | public BinaryOperator> combiner() { 27 | return (list1, list2) -> { 28 | list1.addAll(list2); 29 | return list1; 30 | }; 31 | } 32 | 33 | @Override 34 | public Set characteristics() { 35 | return Collections.unmodifiableSet(EnumSet.of(IDENTITY_FINISH, CONCURRENT)); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap7/ForkJoinSumCalculator.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap7; 2 | 3 | import java.util.concurrent.RecursiveTask; 4 | import java.util.concurrent.ForkJoinTask; 5 | import java.util.stream.LongStream; 6 | 7 | import static lambdasinaction.chap7.ParallelStreamsHarness.FORK_JOIN_POOL; 8 | 9 | public class ForkJoinSumCalculator extends RecursiveTask { 10 | 11 | public static final long THRESHOLD = 10_000; 12 | 13 | private final long[] numbers; 14 | private final int start; 15 | private final int end; 16 | 17 | public ForkJoinSumCalculator(long[] numbers) { 18 | this(numbers, 0, numbers.length); 19 | } 20 | 21 | private ForkJoinSumCalculator(long[] numbers, int start, int end) { 22 | this.numbers = numbers; 23 | this.start = start; 24 | this.end = end; 25 | } 26 | 27 | @Override 28 | protected Long compute() { 29 | int length = end - start; 30 | if (length <= THRESHOLD) { 31 | return computeSequentially(); 32 | } 33 | ForkJoinSumCalculator leftTask = new ForkJoinSumCalculator(numbers, start, start + length/2); 34 | leftTask.fork(); 35 | ForkJoinSumCalculator rightTask = new ForkJoinSumCalculator(numbers, start + length/2, end); 36 | Long rightResult = rightTask.compute(); 37 | Long leftResult = leftTask.join(); 38 | return leftResult + rightResult; 39 | } 40 | 41 | private long computeSequentially() { 42 | long sum = 0; 43 | for (int i = start; i < end; i++) { 44 | sum += numbers[i]; 45 | } 46 | return sum; 47 | } 48 | 49 | public static long forkJoinSum(long n) { 50 | long[] numbers = LongStream.rangeClosed(1, n).toArray(); 51 | ForkJoinTask task = new ForkJoinSumCalculator(numbers); 52 | return FORK_JOIN_POOL.invoke(task); 53 | } 54 | } -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap7/ParallelStreamBenchmark.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap7; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | import java.util.stream.LongStream; 5 | import java.util.stream.Stream; 6 | 7 | import org.openjdk.jmh.annotations.Benchmark; 8 | import org.openjdk.jmh.annotations.BenchmarkMode; 9 | import org.openjdk.jmh.annotations.Fork; 10 | import org.openjdk.jmh.annotations.Level; 11 | import org.openjdk.jmh.annotations.Measurement; 12 | import org.openjdk.jmh.annotations.Mode; 13 | import org.openjdk.jmh.annotations.OutputTimeUnit; 14 | import org.openjdk.jmh.annotations.Scope; 15 | import org.openjdk.jmh.annotations.State; 16 | import org.openjdk.jmh.annotations.TearDown; 17 | import org.openjdk.jmh.annotations.Warmup; 18 | 19 | @State(Scope.Thread) 20 | @BenchmarkMode(Mode.AverageTime) 21 | @OutputTimeUnit(TimeUnit.MILLISECONDS) 22 | @Fork(value=2, jvmArgs={"-Xms4G", "-Xmx4G"}) 23 | @Measurement(iterations=2) 24 | @Warmup(iterations=3) 25 | public class ParallelStreamBenchmark { 26 | 27 | private static final long N = 10_000_000L; 28 | 29 | @Benchmark 30 | public long iterativeSum() { 31 | long result = 0; 32 | for (long i = 1L; i <= N; i++) { 33 | result += i; 34 | } 35 | return result; 36 | } 37 | 38 | @Benchmark 39 | public long sequentialSum() { 40 | return Stream.iterate( 1L, i -> i + 1 ).limit(N).reduce( 0L, Long::sum ); 41 | } 42 | 43 | @Benchmark 44 | public long parallelSum() { 45 | return Stream.iterate(1L, i -> i + 1).limit(N).parallel().reduce( 0L, Long::sum); 46 | } 47 | 48 | @Benchmark 49 | public long rangedSum() { 50 | return LongStream.rangeClosed( 1, N ).reduce( 0L, Long::sum ); 51 | } 52 | 53 | @Benchmark 54 | public long parallelRangedSum() { 55 | return LongStream.rangeClosed(1, N).parallel().reduce( 0L, Long::sum); 56 | } 57 | 58 | @TearDown(Level.Invocation) 59 | public void tearDown() { 60 | System.gc(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap7/ParallelStreams.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap7; 2 | 3 | import java.util.stream.*; 4 | 5 | public class ParallelStreams { 6 | 7 | public static long iterativeSum(long n) { 8 | long result = 0; 9 | for (long i = 0; i <= n; i++) { 10 | result += i; 11 | } 12 | return result; 13 | } 14 | 15 | public static long sequentialSum(long n) { 16 | return Stream.iterate(1L, i -> i + 1).limit(n).reduce(Long::sum).get(); 17 | } 18 | 19 | public static long parallelSum(long n) { 20 | return Stream.iterate(1L, i -> i + 1).limit(n).parallel().reduce(Long::sum).get(); 21 | } 22 | 23 | public static long rangedSum(long n) { 24 | return LongStream.rangeClosed(1, n).reduce(Long::sum).getAsLong(); 25 | } 26 | 27 | public static long parallelRangedSum(long n) { 28 | return LongStream.rangeClosed(1, n).parallel().reduce(Long::sum).getAsLong(); 29 | } 30 | 31 | public static long sideEffectSum(long n) { 32 | Accumulator accumulator = new Accumulator(); 33 | LongStream.rangeClosed(1, n).forEach(accumulator::add); 34 | return accumulator.total; 35 | } 36 | 37 | public static long sideEffectParallelSum(long n) { 38 | Accumulator accumulator = new Accumulator(); 39 | LongStream.rangeClosed(1, n).parallel().forEach(accumulator::add); 40 | return accumulator.total; 41 | } 42 | 43 | public static class Accumulator { 44 | private long total = 0; 45 | 46 | public void add(long value) { 47 | total += value; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap7/ParallelStreamsHarness.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap7; 2 | 3 | import java.util.concurrent.*; 4 | import java.util.function.*; 5 | 6 | public class ParallelStreamsHarness { 7 | 8 | public static final ForkJoinPool FORK_JOIN_POOL = new ForkJoinPool(); 9 | 10 | public static void main(String[] args) { 11 | System.out.println("Iterative Sum done in: " + measurePerf(ParallelStreams::iterativeSum, 10_000_000L) + " msecs"); 12 | System.out.println("Sequential Sum done in: " + measurePerf(ParallelStreams::sequentialSum, 10_000_000L) + " msecs"); 13 | System.out.println("Parallel forkJoinSum done in: " + measurePerf(ParallelStreams::parallelSum, 10_000_000L) + " msecs" ); 14 | System.out.println("Range forkJoinSum done in: " + measurePerf(ParallelStreams::rangedSum, 10_000_000L) + " msecs"); 15 | System.out.println("Parallel range forkJoinSum done in: " + measurePerf(ParallelStreams::parallelRangedSum, 10_000_000L) + " msecs" ); 16 | System.out.println("ForkJoin sum done in: " + measurePerf(ForkJoinSumCalculator::forkJoinSum, 10_000_000L) + " msecs" ); 17 | System.out.println("SideEffect sum done in: " + measurePerf(ParallelStreams::sideEffectSum, 10_000_000L) + " msecs" ); 18 | System.out.println("SideEffect prallel sum done in: " + measurePerf(ParallelStreams::sideEffectParallelSum, 10_000_000L) + " msecs" ); 19 | } 20 | 21 | public static long measurePerf(Function f, T input) { 22 | long fastest = Long.MAX_VALUE; 23 | for (int i = 0; i < 10; i++) { 24 | long start = System.nanoTime(); 25 | R result = f.apply(input); 26 | long duration = (System.nanoTime() - start) / 1_000_000; 27 | System.out.println("Result: " + result); 28 | if (duration < fastest) fastest = duration; 29 | } 30 | return fastest; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap7/WordCount.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap7; 2 | 3 | import java.util.*; 4 | import java.util.function.*; 5 | import java.util.stream.*; 6 | 7 | public class WordCount { 8 | 9 | public static final String SENTENCE = 10 | " Nel mezzo del cammin di nostra vita " + 11 | "mi ritrovai in una selva oscura" + 12 | " che la dritta via era smarrita "; 13 | 14 | public static void main(String[] args) { 15 | System.out.println("Found " + countWordsIteratively(SENTENCE) + " words"); 16 | System.out.println("Found " + countWords(SENTENCE) + " words"); 17 | } 18 | 19 | public static int countWordsIteratively(String s) { 20 | int counter = 0; 21 | boolean lastSpace = true; 22 | for (char c : s.toCharArray()) { 23 | if (Character.isWhitespace(c)) { 24 | lastSpace = true; 25 | } else { 26 | if (lastSpace) counter++; 27 | lastSpace = Character.isWhitespace(c); 28 | } 29 | } 30 | return counter; 31 | } 32 | 33 | public static int countWords(String s) { 34 | //Stream stream = IntStream.range(0, s.length()) 35 | // .mapToObj(SENTENCE::charAt).parallel(); 36 | Spliterator spliterator = new WordCounterSpliterator(s); 37 | Stream stream = StreamSupport.stream(spliterator, true); 38 | 39 | return countWords(stream); 40 | } 41 | 42 | private static int countWords(Stream stream) { 43 | WordCounter wordCounter = stream.reduce(new WordCounter(0, true), 44 | WordCounter::accumulate, 45 | WordCounter::combine); 46 | return wordCounter.getCounter(); 47 | } 48 | 49 | private static class WordCounter { 50 | private final int counter; 51 | private final boolean lastSpace; 52 | 53 | public WordCounter(int counter, boolean lastSpace) { 54 | this.counter = counter; 55 | this.lastSpace = lastSpace; 56 | } 57 | 58 | public WordCounter accumulate(Character c) { 59 | if (Character.isWhitespace(c)) { 60 | return lastSpace ? this : new WordCounter(counter, true); 61 | } else { 62 | return lastSpace ? new WordCounter(counter+1, false) : this; 63 | } 64 | } 65 | 66 | public WordCounter combine(WordCounter wordCounter) { 67 | return new WordCounter(counter + wordCounter.counter, wordCounter.lastSpace); 68 | } 69 | 70 | public int getCounter() { 71 | return counter; 72 | } 73 | } 74 | 75 | private static class WordCounterSpliterator implements Spliterator { 76 | 77 | private final String string; 78 | private int currentChar = 0; 79 | 80 | private WordCounterSpliterator(String string) { 81 | this.string = string; 82 | } 83 | 84 | @Override 85 | public boolean tryAdvance(Consumer action) { 86 | action.accept(string.charAt(currentChar++)); 87 | return currentChar < string.length(); 88 | } 89 | 90 | @Override 91 | public Spliterator trySplit() { 92 | int currentSize = string.length() - currentChar; 93 | if (currentSize < 10) { 94 | return null; 95 | } 96 | for (int splitPos = currentSize / 2 + currentChar; splitPos < string.length(); splitPos++) { 97 | if (Character.isWhitespace(string.charAt(splitPos))) { 98 | Spliterator spliterator = new WordCounterSpliterator(string.substring(currentChar, splitPos)); 99 | currentChar = splitPos; 100 | return spliterator; 101 | } 102 | } 103 | return null; 104 | } 105 | 106 | @Override 107 | public long estimateSize() { 108 | return string.length() - currentChar; 109 | } 110 | 111 | @Override 112 | public int characteristics() { 113 | return ORDERED + SIZED + SUBSIZED + NONNULL + IMMUTABLE; 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap8/ChainOfResponsibilityMain.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap8; 2 | 3 | import java.util.function.Function; 4 | import java.util.function.UnaryOperator; 5 | 6 | 7 | public class ChainOfResponsibilityMain { 8 | 9 | public static void main(String[] args) { 10 | ProcessingObject p1 = new HeaderTextProcessing(); 11 | ProcessingObject p2 = new SpellCheckerProcessing(); 12 | p1.setSuccessor(p2); 13 | String result1 = p1.handle("Aren't labdas really sexy?!!"); 14 | System.out.println(result1); 15 | 16 | 17 | UnaryOperator headerProcessing = 18 | (String text) -> "From Raoul, Mario and Alan: " + text; 19 | UnaryOperator spellCheckerProcessing = 20 | (String text) -> text.replaceAll("labda", "lambda"); 21 | Function pipeline = headerProcessing.andThen(spellCheckerProcessing); 22 | String result2 = pipeline.apply("Aren't labdas really sexy?!!"); 23 | System.out.println(result2); 24 | } 25 | 26 | static private abstract class ProcessingObject { 27 | protected ProcessingObject successor; 28 | 29 | public void setSuccessor(ProcessingObject successor) { 30 | this.successor = successor; 31 | } 32 | 33 | public T handle(T input) { 34 | T r = handleWork(input); 35 | if (successor != null) { 36 | return successor.handle(r); 37 | } 38 | return r; 39 | } 40 | 41 | abstract protected T handleWork(T input); 42 | } 43 | 44 | static private class HeaderTextProcessing 45 | extends ProcessingObject { 46 | public String handleWork(String text) { 47 | return "From Raoul, Mario and Alan: " + text; 48 | } 49 | } 50 | 51 | static private class SpellCheckerProcessing 52 | extends ProcessingObject { 53 | public String handleWork(String text) { 54 | return text.replaceAll("labda", "lambda"); 55 | } 56 | } 57 | } 58 | 59 | 60 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap8/Debugging.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap8; 2 | 3 | 4 | import java.util.*; 5 | 6 | public class Debugging{ 7 | public static void main(String[] args) { 8 | List points = Arrays.asList(new Point(12, 2), null); 9 | points.stream().map(p -> p.getX()).forEach(System.out::println); 10 | } 11 | 12 | 13 | private static class Point{ 14 | private int x; 15 | private int y; 16 | 17 | private Point(int x, int y) { 18 | this.x = x; 19 | this.y = y; 20 | } 21 | 22 | public int getX() { 23 | return x; 24 | } 25 | 26 | public void setX(int x) { 27 | this.x = x; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap8/FactoryMain.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap8; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.function.Supplier; 6 | 7 | 8 | public class FactoryMain { 9 | 10 | public static void main(String[] args) { 11 | Product p1 = ProductFactory.createProduct("loan"); 12 | 13 | Supplier loanSupplier = Loan::new; 14 | Product p2 = loanSupplier.get(); 15 | 16 | Product p3 = ProductFactory.createProductLambda("loan"); 17 | 18 | } 19 | 20 | static private class ProductFactory { 21 | public static Product createProduct(String name){ 22 | switch(name){ 23 | case "loan": return new Loan(); 24 | case "stock": return new Stock(); 25 | case "bond": return new Bond(); 26 | default: throw new RuntimeException("No such product " + name); 27 | } 28 | } 29 | 30 | public static Product createProductLambda(String name){ 31 | Supplier p = map.get(name); 32 | if(p != null) return p.get(); 33 | throw new RuntimeException("No such product " + name); 34 | } 35 | } 36 | 37 | static private interface Product {} 38 | static private class Loan implements Product {} 39 | static private class Stock implements Product {} 40 | static private class Bond implements Product {} 41 | 42 | final static private Map> map = new HashMap<>(); 43 | static { 44 | map.put("loan", Loan::new); 45 | map.put("stock", Stock::new); 46 | map.put("bond", Bond::new); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap8/ObserverMain.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap8; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | 7 | public class ObserverMain { 8 | 9 | public static void main(String[] args) { 10 | Feed f = new Feed(); 11 | f.registerObserver(new NYTimes()); 12 | f.registerObserver(new Guardian()); 13 | f.registerObserver(new LeMonde()); 14 | f.notifyObservers("The queen said her favourite book is Java 8 in Action!"); 15 | 16 | 17 | Feed feedLambda = new Feed(); 18 | 19 | feedLambda.registerObserver((String tweet) -> { 20 | if(tweet != null && tweet.contains("money")){ 21 | System.out.println("Breaking news in NY! " + tweet); } 22 | }); 23 | feedLambda.registerObserver((String tweet) -> { 24 | if(tweet != null && tweet.contains("queen")){ 25 | System.out.println("Yet another news in London... " + tweet); } 26 | }); 27 | 28 | feedLambda.notifyObservers("Money money money, give me money!"); 29 | 30 | } 31 | 32 | 33 | interface Observer{ 34 | void inform(String tweet); 35 | } 36 | 37 | interface Subject{ 38 | void registerObserver(Observer o); 39 | void notifyObservers(String tweet); 40 | } 41 | 42 | static private class NYTimes implements Observer{ 43 | @Override 44 | public void inform(String tweet) { 45 | if(tweet != null && tweet.contains("money")){ 46 | System.out.println("Breaking news in NY!" + tweet); 47 | } 48 | } 49 | } 50 | 51 | static private class Guardian implements Observer{ 52 | @Override 53 | public void inform(String tweet) { 54 | if(tweet != null && tweet.contains("queen")){ 55 | System.out.println("Yet another news in London... " + tweet); 56 | } 57 | } 58 | } 59 | 60 | static private class LeMonde implements Observer{ 61 | @Override 62 | public void inform(String tweet) { 63 | if(tweet != null && tweet.contains("wine")){ 64 | System.out.println("Today cheese, wine and news! " + tweet); 65 | } 66 | } 67 | } 68 | 69 | static private class Feed implements Subject{ 70 | private final List observers = new ArrayList<>(); 71 | public void registerObserver(Observer o) { 72 | this.observers.add(o); 73 | } 74 | public void notifyObservers(String tweet) { 75 | observers.forEach(o -> o.inform(tweet)); 76 | } 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap8/OnlineBanking.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap8; 2 | 3 | 4 | abstract class OnlineBanking { 5 | public void processCustomer(int id){ 6 | Customer c = Database.getCustomerWithId(id); 7 | makeCustomerHappy(c); 8 | } 9 | abstract void makeCustomerHappy(Customer c); 10 | 11 | 12 | // dummy Customer class 13 | static private class Customer {} 14 | // dummy Datbase class 15 | static private class Database{ 16 | static Customer getCustomerWithId(int id){ return new Customer();} 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap8/OnlineBankingLambda.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap8; 2 | 3 | import java.util.function.Consumer; 4 | 5 | 6 | public class OnlineBankingLambda { 7 | 8 | public static void main(String[] args) { 9 | new OnlineBankingLambda().processCustomer(1337, (Customer c) -> System.out.println("Hello!")); 10 | } 11 | 12 | public void processCustomer(int id, Consumer makeCustomerHappy){ 13 | Customer c = Database.getCustomerWithId(id); 14 | makeCustomerHappy.accept(c); 15 | } 16 | 17 | // dummy Customer class 18 | static private class Customer {} 19 | // dummy Database class 20 | static private class Database{ 21 | static Customer getCustomerWithId(int id){ return new Customer();} 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap8/Peek.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap8; 2 | 3 | import java.util.List; 4 | import java.util.stream.Stream; 5 | 6 | import static java.util.stream.Collectors.toList; 7 | 8 | 9 | public class Peek { 10 | 11 | public static void main(String[] args) { 12 | 13 | List result = Stream.of(2, 3, 4, 5) 14 | .peek(x -> System.out.println("taking from stream: " + x)).map(x -> x + 17) 15 | .peek(x -> System.out.println("after map: " + x)).filter(x -> x % 2 == 0) 16 | .peek(x -> System.out.println("after filter: " + x)).limit(3) 17 | .peek(x -> System.out.println("after limit: " + x)).collect(toList()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap8/StrategyMain.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap8; 2 | 3 | 4 | public class StrategyMain { 5 | 6 | public static void main(String[] args) { 7 | // old school 8 | Validator v1 = new Validator(new IsNumeric()); 9 | System.out.println(v1.validate("aaaa")); 10 | Validator v2 = new Validator(new IsAllLowerCase ()); 11 | System.out.println(v2.validate("bbbb")); 12 | 13 | 14 | // with lambdas 15 | Validator v3 = new Validator((String s) -> s.matches("\\d+")); 16 | System.out.println(v3.validate("aaaa")); 17 | Validator v4 = new Validator((String s) -> s.matches("[a-z]+")); 18 | System.out.println(v4.validate("bbbb")); 19 | } 20 | 21 | interface ValidationStrategy { 22 | public boolean execute(String s); 23 | } 24 | 25 | static private class IsAllLowerCase implements ValidationStrategy { 26 | public boolean execute(String s){ 27 | return s.matches("[a-z]+"); 28 | } 29 | } 30 | static private class IsNumeric implements ValidationStrategy { 31 | public boolean execute(String s){ 32 | return s.matches("\\d+"); 33 | } 34 | } 35 | 36 | static private class Validator{ 37 | private final ValidationStrategy strategy; 38 | public Validator(ValidationStrategy v){ 39 | this.strategy = v; 40 | } 41 | public boolean validate(String s){ 42 | return strategy.execute(s); } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap9/Ambiguous.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap9; 2 | 3 | public class Ambiguous{ 4 | 5 | public static void main(String... args) { 6 | new C().hello(); 7 | } 8 | 9 | static interface A{ 10 | public default void hello() { 11 | System.out.println("Hello from A"); 12 | } 13 | } 14 | 15 | static interface B { 16 | public default void hello() { 17 | System.out.println("Hello from B"); 18 | } 19 | } 20 | 21 | static class C implements B, A { 22 | public void hello(){ 23 | A.super.hello(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap9/Diamond.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap9; 2 | 3 | public class Diamond{ 4 | 5 | public static void main(String...args){ 6 | new D().hello(); 7 | } 8 | 9 | static interface A{ 10 | public default void hello(){ 11 | System.out.println("Hello from A"); 12 | } 13 | } 14 | 15 | static interface B extends A { } 16 | 17 | static interface C extends A { 18 | } 19 | 20 | static class D implements B, C { 21 | 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap9/Drawable.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap9; 2 | 3 | /** 4 | * Created by raoul-gabrielurma on 15/01/2014. 5 | */ 6 | public interface Drawable{ 7 | public void draw(); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap9/Ellipse.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap9; 2 | 3 | /** 4 | * Created by raoul-gabrielurma on 15/01/2014. 5 | */ 6 | public class Ellipse implements Resizable { 7 | @Override 8 | public int getWidth() { 9 | return 0; 10 | } 11 | 12 | @Override 13 | public int getHeight() { 14 | return 0; 15 | } 16 | 17 | @Override 18 | public void setWidth(int width) { 19 | 20 | } 21 | 22 | @Override 23 | public void setHeight(int height) { 24 | 25 | } 26 | 27 | @Override 28 | public void setAbsoluteSize(int width, int height) { 29 | 30 | } 31 | 32 | @Override 33 | public void draw() { 34 | 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap9/Game.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap9; 2 | 3 | 4 | import java.util.Arrays; 5 | import java.util.List; 6 | 7 | public class Game { 8 | 9 | public static void main(String...args){ 10 | List resizableShapes = 11 | Arrays.asList(new Square(), 12 | new Triangle(), new Ellipse()); 13 | Utils.paint(resizableShapes); 14 | } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap9/Intro.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap9; 2 | 3 | import java.util.Arrays; 4 | import java.util.Comparator; 5 | import java.util.List; 6 | 7 | public class Intro{ 8 | 9 | public static void main(String...args){ 10 | 11 | List numbers = Arrays.asList(3, 5, 1, 2, 6); 12 | // sort is a default method 13 | // naturalOrder is a static method 14 | numbers.sort(Comparator.naturalOrder()); 15 | System.out.println(numbers); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap9/Letter.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap9; 2 | 3 | import java.util.function.Function; 4 | 5 | public class Letter{ 6 | public static String addHeader(String text){ 7 | return "From Raoul, Mario and Alan:" + text; 8 | } 9 | 10 | public static String addFooter(String text){ 11 | return text + "Kind regards"; 12 | } 13 | 14 | public static String checkSpelling(String text){ 15 | return text.replaceAll("C\\+\\+", "**Censored**"); 16 | } 17 | 18 | 19 | public static void main(String...args){ 20 | Function addHeader = Letter::addHeader; 21 | Function transformationPipeline 22 | = addHeader.andThen(Letter::checkSpelling) 23 | .andThen(Letter::addFooter); 24 | 25 | System.out.println(transformationPipeline.apply("C++ stay away from me!")); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap9/MostSpecific.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap9; 2 | 3 | public class MostSpecific{ 4 | 5 | public static void main(String... args) { 6 | new C().hello(); 7 | new E().hello(); 8 | new G().hello(); 9 | } 10 | 11 | static interface A{ 12 | public default void hello() { 13 | System.out.println("Hello from A"); 14 | } 15 | } 16 | 17 | static interface B extends A{ 18 | public default void hello() { 19 | System.out.println("Hello from B"); 20 | } 21 | } 22 | 23 | static class C implements B, A {} 24 | 25 | static class D implements A{} 26 | 27 | static class E extends D implements B, A{} 28 | 29 | static class F implements B, A { 30 | public void hello() { 31 | System.out.println("Hello from F"); 32 | } 33 | } 34 | 35 | static class G extends F implements B, A{} 36 | 37 | } 38 | 39 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap9/README: -------------------------------------------------------------------------------- 1 | To try out the scenario described in section 8.1 on Evolving APIs you need to do the following: 2 | 3 | compile individual files as follows from the directory src/main/java: 4 | 5 | javac lambdasinaction/chap8/Resizable.java 6 | javac lambdasinaction/chap8/Ellipse.java 7 | javac lambdasinaction/chap8/Utils.java 8 | javac lambdasinaction/chap8/Game.java 9 | 10 | You can run the application and everything will work: 11 | 12 | java lambdasinaction/chap8/Game 13 | 14 | You can now modify the interface Resizable and add the method "setRelativeSize". 15 | Compile and run, no problem: 16 | 17 | javac lambdasinaction/chap8/Resizable.java 18 | 19 | Now modify Utils to use the new setRelativeSize method available on all kinds of Resizable. 20 | Just uncomment the appropriate the line in Utils, compile, run, and you'll have a surprise! 21 | 22 | Exception in thread "main" java.lang.AbstractMethodError: lambdasinaction.chap8.Square.setRelativeSize(II)V 23 | 24 | Note also that recompiling the whole application will fail because Ellipse doesn't implement 25 | the new method setRelativeSize: 26 | 27 | javac lambdasinaction/chap8/Ellipse.java 28 | lambdasinaction/chap7/Ellipse.java:6: error: Ellipse is not abstract and does not override abstract method setRelativeSize(int,int) in Resizable 29 | public class Ellipse implements Resizable { 30 | ^ 31 | 1 error 32 | 33 | The problem can be fixed by ensuring that setRelativeSize is a default method: 34 | 35 | public default void setRelativeSize(int widthFactor, int heightFactor){ 36 | // a default implementation 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap9/Resizable.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap9; 2 | 3 | public interface Resizable extends Drawable{ 4 | public int getWidth(); 5 | public int getHeight(); 6 | public void setWidth(int width); 7 | public void setHeight(int height); 8 | public void setAbsoluteSize(int width, int height); 9 | //TODO: uncomment, read the README for instructions 10 | //public void setRelativeSize(int widthFactor, int heightFactor); 11 | } 12 | 13 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap9/Square.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap9; 2 | 3 | /** 4 | * Created by raoul-gabrielurma on 15/01/2014. 5 | */ 6 | public class Square implements Resizable { 7 | @Override 8 | public int getWidth() { 9 | return 0; 10 | } 11 | 12 | @Override 13 | public int getHeight() { 14 | return 0; 15 | } 16 | 17 | @Override 18 | public void setWidth(int width) { 19 | 20 | } 21 | 22 | @Override 23 | public void setHeight(int height) { 24 | 25 | } 26 | 27 | @Override 28 | public void setAbsoluteSize(int width, int height) { 29 | 30 | } 31 | 32 | @Override 33 | public void draw() { 34 | 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap9/Triangle.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap9; 2 | 3 | /** 4 | * Created by raoul-gabrielurma on 15/01/2014. 5 | */ 6 | public class Triangle implements Resizable { 7 | @Override 8 | public int getWidth() { 9 | return 0; 10 | } 11 | 12 | @Override 13 | public int getHeight() { 14 | return 0; 15 | } 16 | 17 | @Override 18 | public void setWidth(int width) { 19 | 20 | } 21 | 22 | @Override 23 | public void setHeight(int height) { 24 | 25 | } 26 | 27 | @Override 28 | public void setAbsoluteSize(int width, int height) { 29 | 30 | } 31 | 32 | @Override 33 | public void draw() { 34 | 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/chap9/Utils.java: -------------------------------------------------------------------------------- 1 | package lambdasinaction.chap9; 2 | 3 | import java.util.List; 4 | 5 | public class Utils{ 6 | public static void paint(List l){ 7 | l.forEach(r -> { r.setAbsoluteSize(42, 42); }); 8 | 9 | //TODO: uncomment, read the README for instructions 10 | //l.forEach(r -> { r.setRelativeSize(2, 2); }); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/dsl/Grouping.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2005 JBoss Inc 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package lambdasinaction.dsl; 18 | 19 | import lambdasinaction.chap6.Dish; 20 | 21 | import java.util.List; 22 | import java.util.Map; 23 | import java.util.function.Function; 24 | import java.util.stream.Collector; 25 | 26 | import static java.util.stream.Collectors.groupingBy; 27 | import static lambdasinaction.chap6.Dish.menu; 28 | import static lambdasinaction.dsl.Grouping.GroupingBuilder.groupOn; 29 | 30 | public class Grouping { 31 | 32 | enum CaloricLevel { DIET, NORMAL, FAT }; 33 | 34 | public static void main(String ... args) { 35 | System.out.println("Dishes grouped by type and caloric level: " + groupDishedByTypeAndCaloricLevel2()); 36 | System.out.println("Dishes grouped by type and caloric level: " + groupDishedByTypeAndCaloricLevel3()); 37 | } 38 | 39 | private static CaloricLevel getCaloricLevel( Dish dish ) { 40 | if (dish.getCalories() <= 400) return CaloricLevel.DIET; 41 | else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL; 42 | else return CaloricLevel.FAT; 43 | } 44 | 45 | private static Map>> groupDishedByTypeAndCaloricLevel2() { 46 | return menu.stream().collect( 47 | twoLevelGroupingBy(Dish::getType, dish -> getCaloricLevel( dish ) ) 48 | ); 49 | } 50 | 51 | public static Collector>>> twoLevelGroupingBy(Function f1, Function f2) { 52 | return groupingBy(f1, groupingBy(f2)); 53 | } 54 | 55 | private static Map>> groupDishedByTypeAndCaloricLevel3() { 56 | Collector>>> c = groupOn( ( Dish dish ) -> getCaloricLevel( dish ) ).after( Dish::getType ).get(); 57 | return menu.stream().collect( c ); 58 | } 59 | 60 | public static class GroupingBuilder { 61 | private final Collector> collector; 62 | 63 | public GroupingBuilder( Collector> collector ) { 64 | this.collector = collector; 65 | } 66 | 67 | public Collector> get() { 68 | return collector; 69 | } 70 | 71 | public GroupingBuilder, J> after(Function classifier) { 72 | return new GroupingBuilder<>( groupingBy( classifier, collector ) ); 73 | } 74 | 75 | public static GroupingBuilder, K> groupOn(Function classifier) { 76 | return new GroupingBuilder<>( groupingBy( classifier ) ); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/dsl/LambdaOrderBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2005 JBoss Inc 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package lambdasinaction.dsl; 18 | 19 | import lambdasinaction.dsl.model.Order; 20 | import lambdasinaction.dsl.model.Stock; 21 | import lambdasinaction.dsl.model.Trade; 22 | 23 | import java.util.function.Consumer; 24 | 25 | public class LambdaOrderBuilder { 26 | 27 | private Order order = new Order(); 28 | 29 | public static Order order(Consumer consumer) { 30 | LambdaOrderBuilder builder = new LambdaOrderBuilder(); 31 | consumer.accept( builder ); 32 | return builder.order; 33 | } 34 | 35 | public void forCustomer(String customer) { 36 | order.setCustomer( customer ); 37 | } 38 | 39 | public void buy(Consumer consumer) { 40 | trade( consumer, Trade.Type.BUY ); 41 | } 42 | 43 | public void sell(Consumer consumer) { 44 | trade( consumer, Trade.Type.SELL ); 45 | } 46 | 47 | private void trade( Consumer consumer, Trade.Type type ) { 48 | TradeBuilder builder = new TradeBuilder(); 49 | builder.trade.setType( type ); 50 | consumer.accept( builder ); 51 | order.addTrade( builder.trade ); 52 | } 53 | 54 | public static class TradeBuilder { 55 | private Trade trade = new Trade(); 56 | 57 | public void quantity(int quantity) { 58 | trade.setQuantity( quantity ); 59 | } 60 | 61 | public void price(double price) { 62 | trade.setPrice( price ); 63 | } 64 | 65 | public void stock(Consumer consumer) { 66 | StockBuilder builder = new StockBuilder(); 67 | consumer.accept( builder ); 68 | trade.setStock( builder.stock ); 69 | } 70 | } 71 | 72 | public static class StockBuilder { 73 | private Stock stock = new Stock(); 74 | 75 | public void symbol(String symbol) { 76 | stock.setSymbol( symbol ); 77 | } 78 | 79 | public void market(String market) { 80 | stock.setMarket( market ); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/dsl/Main.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2005 JBoss Inc 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package lambdasinaction.dsl; 18 | 19 | import lambdasinaction.dsl.model.Order; 20 | import lambdasinaction.dsl.model.Stock; 21 | import lambdasinaction.dsl.model.Trade; 22 | 23 | import static lambdasinaction.dsl.MethodChainingOrderBuilder.forCustomer; 24 | import static lambdasinaction.dsl.NestedFunctionOrderBuilder.*; 25 | 26 | public class Main { 27 | 28 | public void plain() { 29 | Order order = new Order(); 30 | order.setCustomer( "BigBank" ); 31 | 32 | Trade trade1 = new Trade(); 33 | trade1.setType( Trade.Type.BUY ); 34 | 35 | Stock stock1 = new Stock(); 36 | stock1.setSymbol( "IBM" ); 37 | stock1.setMarket( "NYSE" ); 38 | 39 | trade1.setStock( stock1 ); 40 | trade1.setPrice( 125.00 ); 41 | trade1.setQuantity( 80 ); 42 | order.addTrade( trade1 ); 43 | 44 | Trade trade2 = new Trade(); 45 | trade2.setType( Trade.Type.BUY ); 46 | 47 | Stock stock2 = new Stock(); 48 | stock2.setSymbol( "GOOGLE" ); 49 | stock2.setMarket( "NASDAQ" ); 50 | 51 | trade2.setStock( stock2 ); 52 | trade2.setPrice( 375.00 ); 53 | trade2.setQuantity( 50 ); 54 | order.addTrade( trade2 ); 55 | } 56 | 57 | public void methodChaining() { 58 | Order order = forCustomer( "BigBank" ) 59 | .buy( 80 ).stock( "IBM" ).on( "NYSE" ).at( 125.00 ) 60 | .sell( 50 ).stock( "GOOGLE" ).on( "NASDAQ" ).at( 375.00 ) 61 | .end(); 62 | 63 | } 64 | 65 | public void nestedFunction() { 66 | Order order = order("BigBank", 67 | buy(80, 68 | stock( "IBM", on( "NYSE" ) ), 69 | at(125.00)), 70 | sell(50, 71 | stock("GOOGLE", on("NASDAQ")), 72 | at(375.00)) 73 | ); 74 | } 75 | 76 | public void lambda() { 77 | Order order = LambdaOrderBuilder.order( o -> { 78 | o.forCustomer( "BigBank" ); 79 | o.buy( t -> { 80 | t.quantity( 80 ); 81 | t.price( 125.00 ); 82 | t.stock( s -> { 83 | s.symbol( "IBM" ); 84 | s.market( "NYSE" ); 85 | } ); 86 | }); 87 | o.sell( t -> { 88 | t.quantity( 50 ); 89 | t.price( 375.00 ); 90 | t.stock( s -> { 91 | s.symbol( "GOOGLE" ); 92 | s.market( "NASDAQ" ); 93 | } ); 94 | }); 95 | } ); 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/dsl/MethodChainingOrderBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2005 JBoss Inc 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package lambdasinaction.dsl; 18 | 19 | import lambdasinaction.dsl.model.Order; 20 | import lambdasinaction.dsl.model.Stock; 21 | import lambdasinaction.dsl.model.Trade; 22 | 23 | public class MethodChainingOrderBuilder { 24 | 25 | public final Order order = new Order(); 26 | 27 | private MethodChainingOrderBuilder(String customer) { 28 | order.setCustomer( customer ); 29 | } 30 | 31 | public static MethodChainingOrderBuilder forCustomer( String customer ) { 32 | return new MethodChainingOrderBuilder(customer); 33 | } 34 | 35 | public Order end() { 36 | return order; 37 | } 38 | 39 | public TradeBuilder buy(int quantity) { 40 | return new TradeBuilder( this, Trade.Type.BUY, quantity ); 41 | } 42 | 43 | public TradeBuilder sell(int quantity) { 44 | return new TradeBuilder( this, Trade.Type.SELL, quantity ); 45 | } 46 | 47 | private MethodChainingOrderBuilder addTrade(Trade trade) { 48 | order.addTrade( trade ); 49 | return this; 50 | } 51 | 52 | public static class TradeBuilder { 53 | private final MethodChainingOrderBuilder builder; 54 | public final Trade trade = new Trade(); 55 | 56 | private TradeBuilder(MethodChainingOrderBuilder builder, Trade.Type type, int quantity) { 57 | this.builder = builder; 58 | trade.setType( type ); 59 | trade.setQuantity( quantity ); 60 | } 61 | 62 | public StockBuilder stock(String symbol) { 63 | return new StockBuilder( builder, trade, symbol ); 64 | } 65 | } 66 | 67 | public static class TradeBuilderWithStock { 68 | private final MethodChainingOrderBuilder builder; 69 | private final Trade trade; 70 | 71 | public TradeBuilderWithStock( MethodChainingOrderBuilder builder, Trade trade ) { 72 | this.builder = builder; 73 | this.trade = trade; 74 | } 75 | 76 | public MethodChainingOrderBuilder at(double price) { 77 | trade.setPrice( price ); 78 | return builder.addTrade( trade ); 79 | } 80 | } 81 | 82 | public static class StockBuilder { 83 | private final MethodChainingOrderBuilder builder; 84 | private final Trade trade; 85 | private final Stock stock = new Stock(); 86 | 87 | private StockBuilder(MethodChainingOrderBuilder builder, Trade trade, String symbol) { 88 | this.builder = builder; 89 | this.trade = trade; 90 | stock.setSymbol( symbol ); 91 | } 92 | 93 | public TradeBuilderWithStock on(String market) { 94 | stock.setMarket( market ); 95 | trade.setStock( stock ); 96 | return new TradeBuilderWithStock( builder, trade ); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/dsl/Mixed.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2005 JBoss Inc 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package lambdasinaction.dsl; 18 | 19 | import lambdasinaction.dsl.model.Order; 20 | 21 | import static lambdasinaction.dsl.MixedBuilder.buy; 22 | import static lambdasinaction.dsl.MixedBuilder.sell; 23 | import static lambdasinaction.dsl.MixedBuilder.forCustomer; 24 | 25 | public class Mixed { 26 | public void mixed() { 27 | Order order = 28 | forCustomer( "BigBank", 29 | buy( t -> t.quantity( 80 ) 30 | .stock( "IBM" ) 31 | .on( "NYSE" ) 32 | .at( 125.00 )), 33 | sell( t -> t.quantity( 50 ) 34 | .stock( "GOOGLE" ) 35 | .on( "NASDAQ" ) 36 | .at( 125.00 )) ); 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/dsl/MixedBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2005 JBoss Inc 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package lambdasinaction.dsl; 18 | 19 | import lambdasinaction.dsl.model.Order; 20 | import lambdasinaction.dsl.model.Stock; 21 | import lambdasinaction.dsl.model.Trade; 22 | 23 | import java.util.function.Consumer; 24 | import java.util.stream.Stream; 25 | 26 | public class MixedBuilder { 27 | 28 | public static Order forCustomer(String customer, TradeBuilder... builders) { 29 | Order order = new Order(); 30 | order.setCustomer( customer ); 31 | Stream.of(builders).forEach( b -> order.addTrade( b.trade ) ); 32 | return order; 33 | } 34 | 35 | public static TradeBuilder buy(Consumer consumer) { 36 | return buildTrade( consumer, Trade.Type.BUY ); 37 | } 38 | 39 | public static TradeBuilder sell(Consumer consumer) { 40 | return buildTrade( consumer, Trade.Type.SELL ); 41 | } 42 | 43 | private static TradeBuilder buildTrade( Consumer consumer, Trade.Type buy ) { 44 | TradeBuilder builder = new TradeBuilder(); 45 | builder.trade.setType( buy ); 46 | consumer.accept( builder ); 47 | return builder; 48 | } 49 | 50 | public static class TradeBuilder { 51 | private Trade trade = new Trade(); 52 | 53 | public TradeBuilder quantity(int quantity) { 54 | trade.setQuantity( quantity ); 55 | return this; 56 | } 57 | 58 | public TradeBuilder at(double price) { 59 | trade.setPrice( price ); 60 | return this; 61 | } 62 | 63 | public StockBuilder stock(String symbol) { 64 | return new StockBuilder(this, trade, symbol); 65 | } 66 | } 67 | 68 | public static class StockBuilder { 69 | private final TradeBuilder builder; 70 | private final Trade trade; 71 | private final Stock stock = new Stock(); 72 | 73 | private StockBuilder(TradeBuilder builder, Trade trade, String symbol) { 74 | this.builder = builder; 75 | this.trade = trade; 76 | stock.setSymbol( symbol ); 77 | } 78 | 79 | public TradeBuilder on(String market) { 80 | stock.setMarket( market ); 81 | trade.setStock( stock ); 82 | return builder; 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/dsl/NestedFunctionOrderBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2005 JBoss Inc 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package lambdasinaction.dsl; 18 | 19 | import lambdasinaction.dsl.model.Order; 20 | import lambdasinaction.dsl.model.Stock; 21 | import lambdasinaction.dsl.model.Trade; 22 | 23 | import java.util.stream.Stream; 24 | 25 | public class NestedFunctionOrderBuilder { 26 | 27 | public static Order order(String customer, Trade... trades) { 28 | Order order = new Order(); 29 | order.setCustomer( customer ); 30 | Stream.of(trades).forEach( order::addTrade ); 31 | return order; 32 | } 33 | 34 | public static Trade buy(int quantity, Stock stock, double price) { 35 | return buildTrade( stock, price, Trade.Type.BUY ); 36 | } 37 | 38 | public static Trade sell(int quantity, Stock stock, double price) { 39 | return buildTrade( stock, price, Trade.Type.SELL ); 40 | } 41 | 42 | private static Trade buildTrade( Stock stock, double price, Trade.Type buy ) { 43 | Trade trade = new Trade(); 44 | trade.setType( buy ); 45 | trade.setStock( stock ); 46 | trade.setPrice( price ); 47 | return trade; 48 | } 49 | 50 | public static double at(double price) { 51 | return price; 52 | } 53 | 54 | public static Stock stock(String symbol, String market) { 55 | Stock stock = new Stock(); 56 | stock.setSymbol( symbol ); 57 | stock.setMarket( market ); 58 | return stock; 59 | } 60 | 61 | public static String on(String market) { 62 | return market; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/dsl/TaxCalculator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2005 JBoss Inc 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package lambdasinaction.dsl; 18 | 19 | import lambdasinaction.dsl.model.Order; 20 | import lambdasinaction.dsl.model.Tax; 21 | 22 | import java.util.function.Function; 23 | 24 | public class TaxCalculator { 25 | 26 | public static double calculate( Order order, boolean useRegional, boolean useGeneral, boolean useSurcharge ) { 27 | double value = order.getValue(); 28 | if (useRegional) value = Tax.regional(value); 29 | if (useGeneral) value = Tax.general(value); 30 | if (useSurcharge) value = Tax.surcharge(value); 31 | return value; 32 | } 33 | 34 | private boolean useRegional; 35 | private boolean useGeneral; 36 | private boolean useSurcharge; 37 | 38 | public TaxCalculator withTaxRegional() { 39 | useRegional = true; 40 | return this; 41 | } 42 | 43 | public TaxCalculator withTaxGeneral() { 44 | useGeneral= true; 45 | return this; 46 | } 47 | 48 | public TaxCalculator withTaxSurcharge() { 49 | useSurcharge = true; 50 | return this; 51 | } 52 | 53 | public double calculate(Order order) { 54 | return calculate( order, useRegional, useGeneral, useSurcharge ); 55 | } 56 | 57 | public Function taxFuncion = Function.identity(); 58 | 59 | public TaxCalculator with(Function f) { 60 | taxFuncion.andThen( f ); 61 | return this; 62 | } 63 | 64 | public double calculateF(Order order) { 65 | return taxFuncion.apply( order.getValue() ); 66 | } 67 | 68 | public static void main(String[] args) { 69 | Order order = new Order(); 70 | 71 | double value = TaxCalculator.calculate( order, true, false, true ); 72 | 73 | value = new TaxCalculator().withTaxRegional() 74 | .withTaxSurcharge() 75 | .calculate( order ); 76 | 77 | value = new TaxCalculator().with(Tax::regional) 78 | .with(Tax::surcharge) 79 | .calculate( order ); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/dsl/model/Order.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2005 JBoss Inc 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package lambdasinaction.dsl.model; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | public class Order { 23 | 24 | private String customer; 25 | 26 | private List trades = new ArrayList<>(); 27 | 28 | public void addTrade( Trade trade ) { 29 | trades.add( trade ); 30 | } 31 | 32 | public String getCustomer() { 33 | return customer; 34 | } 35 | 36 | public void setCustomer( String customer ) { 37 | this.customer = customer; 38 | } 39 | 40 | public double getValue() { 41 | return trades.stream().mapToDouble( Trade::getValue ).sum(); 42 | } 43 | } -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/dsl/model/Stock.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2005 JBoss Inc 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package lambdasinaction.dsl.model; 18 | 19 | public class Stock { 20 | 21 | private String symbol; 22 | 23 | private String market; 24 | 25 | public String getSymbol() { 26 | return symbol; 27 | } 28 | 29 | public void setSymbol( String symbol ) { 30 | this.symbol = symbol; 31 | } 32 | 33 | public String getMarket() { 34 | return market; 35 | } 36 | 37 | public void setMarket( String market ) { 38 | this.market = market; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/dsl/model/Tax.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2005 JBoss Inc 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package lambdasinaction.dsl.model; 18 | 19 | public class Tax { 20 | public static double regional(double value) { 21 | return value * 1.1; 22 | } 23 | 24 | public static double general(double value) { 25 | return value * 1.3; 26 | } 27 | 28 | public static double surcharge(double value) { 29 | return value * 1.05; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/lambdasinaction/dsl/model/Trade.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2005 JBoss Inc 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package lambdasinaction.dsl.model; 18 | 19 | public class Trade { 20 | 21 | public enum Type { BUY, SELL } 22 | 23 | private Type type; 24 | 25 | private Stock stock; 26 | 27 | private int quantity; 28 | 29 | private double price; 30 | 31 | public Type getType() { 32 | return type; 33 | } 34 | 35 | public void setType( Type type ) { 36 | this.type = type; 37 | } 38 | 39 | public int getQuantity() { 40 | return quantity; 41 | } 42 | 43 | public void setQuantity( int quantity ) { 44 | this.quantity = quantity; 45 | } 46 | 47 | public double getPrice() { 48 | return price; 49 | } 50 | 51 | public void setPrice( double price ) { 52 | this.price = price; 53 | } 54 | 55 | public Stock getStock() { 56 | return stock; 57 | } 58 | 59 | public void setStock( Stock stock ) { 60 | this.stock = stock; 61 | } 62 | 63 | public double getValue() { 64 | return quantity * price; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/resources/lambdasinaction/chap3/data.txt: -------------------------------------------------------------------------------- 1 | Java 2 | 8 3 | Lambdas 4 | In 5 | Action -------------------------------------------------------------------------------- /src/main/resources/lambdasinaction/chap5/data.txt: -------------------------------------------------------------------------------- 1 | The quick brown fox jumped over the lazy dog 2 | The lazy dog jumped over the quick brown fox 3 | --------------------------------------------------------------------------------