├── .gitignore ├── LICENSE ├── LambdaLab ├── SonnetI.txt ├── build.xml ├── nbproject │ ├── build-impl.xml │ ├── cfg_hints.xml │ ├── genfiles.properties │ ├── project.properties │ └── project.xml ├── src │ └── support │ │ ├── Dummy.java │ │ └── Grind.java └── test │ ├── exercises │ ├── A_Lambdas.java │ ├── B_Comparators.java │ ├── C_DefaultMethods.java │ ├── D_SimpleStreams.java │ ├── E_IntermediateStreams.java │ ├── F_AdvancedStreams.java │ ├── G_MatcherScanner.java │ └── H_Challenges.java │ ├── model │ └── Person.java │ ├── solutions │ ├── A_Lambdas.java │ ├── B_Comparators.java │ ├── C_DefaultMethods.java │ ├── D_SimpleStreams.java │ ├── E_IntermediateStreams.java │ ├── F_AdvancedStreams.java │ ├── G_MatcherScanner.java │ ├── H_Challenges.java │ └── cleanit │ └── suite │ └── JUnit4TestSuite.java ├── README.html └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated Files # 2 | *.class 3 | *~ 4 | *.orig 5 | *.rej 6 | *# 7 | 8 | # Package Files # 9 | *.jar 10 | *.war 11 | *.ear 12 | 13 | # NetBeans Files # 14 | /LambdaLab/build/ 15 | /LambdaLab/dist/ 16 | /LambdaLab/nbproject/private/ 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions 5 | are met: 6 | 7 | - Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | - Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | - Neither the name of Oracle nor the names of its 15 | contributors may be used to endorse or promote products derived 16 | from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 19 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 20 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 22 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 23 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 24 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 26 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | This source code is provided to illustrate the usage of a given feature 31 | or technique and has been deliberately simplified. Additional steps 32 | required for a production-quality application, such as security checks, 33 | input validation, and proper error handling, might not be present in 34 | this sample code. 35 | -------------------------------------------------------------------------------- /LambdaLab/SonnetI.txt: -------------------------------------------------------------------------------- 1 | From fairest creatures we desire increase, 2 | That thereby beauty's rose might never die, 3 | But as the riper should by time decease, 4 | His tender heir might bear his memory: 5 | But thou contracted to thine own bright eyes, 6 | Feed'st thy light's flame with self-substantial fuel, 7 | Making a famine where abundance lies, 8 | Thy self thy foe, to thy sweet self too cruel: 9 | Thou that art now the world's fresh ornament, 10 | And only herald to the gaudy spring, 11 | Within thine own bud buriest thy content, 12 | And, tender churl, mak'st waste in niggarding: 13 | Pity the world, or else this glutton be, 14 | To eat the world's due, by the grave and thee. 15 | -------------------------------------------------------------------------------- /LambdaLab/build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Builds, tests, and runs the project LambdaHOL. 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 81 | 82 | -------------------------------------------------------------------------------- /LambdaLab/nbproject/cfg_hints.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /LambdaLab/nbproject/genfiles.properties: -------------------------------------------------------------------------------- 1 | build.xml.data.CRC32=47eb4ba1 2 | build.xml.script.CRC32=125f467b 3 | build.xml.stylesheet.CRC32=8064a381@1.68.0.46 4 | # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. 5 | # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. 6 | nbproject/build-impl.xml.data.CRC32=acb17315 7 | nbproject/build-impl.xml.script.CRC32=6ab48ba6 8 | nbproject/build-impl.xml.stylesheet.CRC32=3a2fa800@1.89.1.48 9 | -------------------------------------------------------------------------------- /LambdaLab/nbproject/project.properties: -------------------------------------------------------------------------------- 1 | annotation.processing.enabled=true 2 | annotation.processing.enabled.in.editor=false 3 | annotation.processing.processors.list= 4 | annotation.processing.run.all.processors=true 5 | annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output 6 | application.title=LambdaLab 7 | application.vendor=smarks 8 | auxiliary.org-netbeans-spi-editor-hints-projects.perProjectHintSettingsEnabled=true 9 | auxiliary.org-netbeans-spi-editor-hints-projects.perProjectHintSettingsFile=nbproject/cfg_hints.xml 10 | build.classes.dir=${build.dir}/classes 11 | build.classes.excludes=**/*.java,**/*.form 12 | # This directory is removed when the project is cleaned: 13 | build.dir=build 14 | build.generated.dir=${build.dir}/generated 15 | build.generated.sources.dir=${build.dir}/generated-sources 16 | # Only compile against the classpath explicitly listed here: 17 | build.sysclasspath=ignore 18 | build.test.classes.dir=${build.dir}/test/classes 19 | build.test.results.dir=${build.dir}/test/results 20 | # Uncomment to specify the preferred debugger connection transport: 21 | #debug.transport=dt_socket 22 | debug.classpath=\ 23 | ${run.classpath} 24 | debug.modulepath=\ 25 | ${run.modulepath} 26 | debug.test.classpath=\ 27 | ${run.test.classpath} 28 | debug.test.modulepath=\ 29 | ${run.test.modulepath} 30 | # Files in build.classes.dir which should be excluded from distribution jar 31 | dist.archive.excludes= 32 | # This directory is removed when the project is cleaned: 33 | dist.dir=dist 34 | dist.jar=${dist.dir}/LambdaLab.jar 35 | dist.javadoc.dir=${dist.dir}/javadoc 36 | endorsed.classpath= 37 | excludes= 38 | includes=** 39 | jar.compress=false 40 | javac.classpath= 41 | # Space-separated list of extra javac options 42 | javac.compilerargs= 43 | javac.deprecation=false 44 | javac.external.vm=false 45 | javac.modulepath= 46 | javac.processormodulepath= 47 | javac.processorpath=\ 48 | ${javac.classpath} 49 | javac.source=9 50 | javac.target=9 51 | javac.test.classpath=\ 52 | ${javac.classpath}:\ 53 | ${build.classes.dir}:\ 54 | ${libs.junit_4.classpath}:\ 55 | ${libs.hamcrest.classpath} 56 | javac.test.modulepath=\ 57 | ${javac.modulepath} 58 | javac.test.processorpath=\ 59 | ${javac.test.classpath} 60 | javadoc.additionalparam= 61 | javadoc.author=false 62 | javadoc.encoding=${source.encoding} 63 | javadoc.html5=false 64 | javadoc.noindex=false 65 | javadoc.nonavbar=false 66 | javadoc.notree=false 67 | javadoc.private=false 68 | javadoc.splitindex=true 69 | javadoc.use=true 70 | javadoc.version=false 71 | javadoc.windowtitle= 72 | jlink.launcher=false 73 | jlink.launcher.name=LambdaLab 74 | main.class=support.Dummy 75 | meta.inf.dir=${src.dir}/META-INF 76 | mkdist.disabled=true 77 | platform.active=default_platform 78 | run.classpath=\ 79 | ${javac.classpath}:\ 80 | ${build.classes.dir} 81 | # Space-separated list of JVM arguments used when running the project. 82 | # You may also define separate properties like run-sys-prop.name=value instead of -Dname=value. 83 | # To set system properties for unit tests define test-sys-prop.name=value: 84 | run.jvmargs= 85 | run.modulepath=\ 86 | ${javac.modulepath} 87 | run.test.classpath=\ 88 | ${javac.test.classpath}:\ 89 | ${build.test.classes.dir} 90 | run.test.modulepath=\ 91 | ${javac.test.modulepath} 92 | source.encoding=UTF-8 93 | src.dir=src 94 | test.src.dir=test 95 | -------------------------------------------------------------------------------- /LambdaLab/nbproject/project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | org.netbeans.modules.java.j2seproject 4 | 5 | 6 | LambdaLab 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /LambdaLab/src/support/Dummy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | 7 | package support; 8 | 9 | /** 10 | * Dummy main class; does nothing, but enables tests to run. 11 | */ 12 | public class Dummy { 13 | public static void main(String[] args) { 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /LambdaLab/src/support/Grind.java: -------------------------------------------------------------------------------- 1 | package support; 2 | 3 | import java.io.IOException; 4 | import java.io.PrintStream; 5 | import java.io.UncheckedIOException; 6 | import java.nio.file.Files; 7 | import java.nio.file.Path; 8 | import java.nio.file.Paths; 9 | import java.util.function.Consumer; 10 | import java.util.stream.Stream; 11 | 12 | /** 13 | * Process all files test/solutions/*.java into 14 | * corresponding files in test/exercises/*.java. 15 | */ 16 | public class Grind { 17 | static class Sink implements Consumer { 18 | boolean removing = false; 19 | final PrintStream out; 20 | Sink(PrintStream out) { this.out = out; } 21 | public void accept(String line) { 22 | if (line.contains("//BEGINREMOVE")) { 23 | removing = true; 24 | } else if (line.contains("//ENDREMOVE")) { 25 | removing = false; 26 | } else if (!removing) { 27 | out.println(line); 28 | } 29 | } 30 | } 31 | 32 | String subst(String line) { 33 | return line.replace("package solutions;", "package exercises;") 34 | .replace("@Test", "@Test @Ignore") 35 | .replace("//UNCOMMENT//", "") 36 | .replaceFirst("^(.*)//TODO//(.*)$", "$1$2 // TODO"); 37 | } 38 | 39 | void processFile(Path input) { 40 | Path output = Paths.get("test", "exercises") 41 | .resolve(input.getName(input.getNameCount() - 1)); 42 | System.out.println(input + " => " + output); 43 | 44 | try (Stream lines = Files.lines(input); 45 | PrintStream out = new PrintStream(output.toFile())) { 46 | lines.map(this::subst) 47 | .forEachOrdered(new Sink(out)); 48 | } catch (IOException ioe) { 49 | throw new UncheckedIOException(ioe); 50 | } 51 | } 52 | 53 | void run() throws IOException { 54 | Path dir = Paths.get("test", "solutions"); 55 | try (Stream paths = Files.list(dir)) { 56 | paths.filter(p -> p.toString().endsWith(".java")) 57 | .forEachOrdered(this::processFile); 58 | } 59 | } 60 | 61 | public static void main(String[] args) throws IOException { 62 | new Grind().run(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /LambdaLab/test/exercises/A_Lambdas.java: -------------------------------------------------------------------------------- 1 | package exercises; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | import java.util.function.BiFunction; 7 | 8 | import java.util.function.Consumer; 9 | import java.util.function.Function; 10 | import java.util.function.Predicate; 11 | import java.util.function.Supplier; 12 | 13 | import org.junit.Ignore; 14 | import org.junit.Test; 15 | 16 | import static org.junit.Assert.assertEquals; 17 | import static org.junit.Assert.assertFalse; 18 | import static org.junit.Assert.assertTrue; 19 | 20 | /** 21 | * This set of exercises is about lambdas and method references. 22 | * You will write a lambda or method reference corresponding to 23 | * each of several different functional interfaces. Each exercise 24 | * is named after the functional interface intended to be used 25 | * as the solution. 26 | */ 27 | public class A_Lambdas { 28 | 29 | /** 30 | * Write a lambda expression that is a predicate 31 | * that tests whether a string is longer than four characters. 32 | */ 33 | @Test @Ignore 34 | public void a_predicate1() { 35 | Predicate pred = null; // TODO 36 | 37 | assertTrue(pred.test("abcde")); 38 | assertFalse(pred.test("abcd")); 39 | } 40 | 41 | /** 42 | * Write a lambda expression that is a predicate 43 | * that tests whether a string is empty. 44 | */ 45 | @Test @Ignore 46 | public void a_predicate2() { 47 | Predicate pred = null; // TODO 48 | 49 | assertTrue(pred.test("")); 50 | assertFalse(pred.test("a")); 51 | } 52 | 53 | /** 54 | * Write an unbound method reference that is a predicate 55 | * that tests whether a string is empty. An unbound method 56 | * reference has a class name on the left-hand side of the :: 57 | * operator: 58 | * 59 | * classname::methodname 60 | */ 61 | @Test @Ignore 62 | public void a_predicate3() { 63 | Predicate pred = null; // TODO 64 | 65 | assertTrue(pred.test("")); 66 | assertFalse(pred.test("a")); 67 | } 68 | // Hint: 69 | // 70 | // Copy the lambda expression from the previous exercise and then pop 71 | // up the menu over the "light bulb" icon in the left margin. This menu 72 | // has an option to convert the lambda to a method reference. (The exact 73 | // gesture will vary among IDEs.) 74 | // 75 | 76 | /** 77 | * Create a predicate that returns true if both predicates 78 | * startsWithJ and lengthIs7 hold. 79 | */ 80 | @Test @Ignore 81 | public void a_predicate4() { 82 | Predicate startsWithJ = s -> s.startsWith("J"); 83 | Predicate lengthIs7 = s -> s.length() == 7; 84 | 85 | Predicate startsWithJAndLengthIs7 = null; // TODO 86 | 87 | assertFalse(startsWithJAndLengthIs7.test("Hello")); 88 | assertFalse(startsWithJAndLengthIs7.test("HelloJ1")); 89 | assertFalse(startsWithJAndLengthIs7.test("Java1")); 90 | assertTrue(startsWithJAndLengthIs7.test("JavaOne")); 91 | } 92 | // Hint: 93 | // 94 | // see java.util.function.Predicate.and() 95 | // 96 | 97 | /** 98 | * Create a predicate that is true if the length of the provided string 99 | * is 9 or the provided string equals ERROR. 100 | */ 101 | @Test @Ignore 102 | public void a_predicate5() { 103 | Predicate lengthIs9 = s -> s.length() == 9; 104 | Predicate equalsError = "ERROR"::equals; 105 | // Note: this could also be: Predicate.isEqual("ERROR") 106 | 107 | Predicate lengthIs9orError = null; // TODO 108 | 109 | assertFalse(lengthIs9orError.test("Hello")); 110 | assertTrue(lengthIs9orError.test("Hello J1!")); 111 | assertTrue(lengthIs9orError.test("ERROR")); 112 | assertFalse(lengthIs9orError.test("Error")); 113 | } 114 | // Hint: 115 | // 116 | // see java.util.function.Predicate.or() 117 | // 118 | 119 | /** 120 | * Write a lambda expression that wraps the given 121 | * string in parentheses. 122 | */ 123 | @Test @Ignore 124 | public void b_function1() { 125 | Function func = null; // TODO 126 | 127 | assertEquals("(abc)", func.apply("abc")); 128 | } 129 | 130 | /** 131 | * Write a lambda expression that converts the 132 | * given string to upper case. 133 | */ 134 | @Test @Ignore 135 | public void b_function2() { 136 | Function func = null; // TODO 137 | 138 | assertEquals("ABC", func.apply("abc")); 139 | } 140 | 141 | /** 142 | * Write an unbound method reference that converts the 143 | * given string to upper case. 144 | */ 145 | @Test @Ignore 146 | public void b_function3() { 147 | Function func = null; // TODO 148 | 149 | assertEquals("ABC", func.apply("abc")); 150 | } 151 | 152 | /** 153 | * Given two Functions, one that converts a null reference to an 154 | * empty string, and another that gets the length of a string, 155 | * create a single function converts nulls and then gets the 156 | * string's length. 157 | */ 158 | @Test @Ignore 159 | public void b_function4() { 160 | Function unNullify = s -> s == null ? "" : s; 161 | Function length = String::length; 162 | 163 | Function lengthBis = null; // TODO 164 | 165 | assertEquals((Integer)14, lengthBis.apply("Hello JavaOne!")); 166 | assertEquals((Integer)0, lengthBis.apply("")); 167 | assertEquals((Integer)0, lengthBis.apply(null)); 168 | } 169 | // Hint: 170 | // 171 | // See java.util.Function.andThen() or java.util.Function.compose() 172 | // 173 | 174 | /** 175 | * Write a lambda expression that appends the 176 | * string "abc" to the given StringBuilder. 177 | */ 178 | @Test @Ignore 179 | public void c_consumer1() { 180 | Consumer cons = null; // TODO 181 | 182 | StringBuilder sb = new StringBuilder("xyz"); 183 | cons.accept(sb); 184 | assertEquals("xyzabc", sb.toString()); 185 | } 186 | 187 | /** 188 | * Write a lambda expression that clears the given list. 189 | */ 190 | @Test @Ignore 191 | public void c_consumer2() { 192 | Consumer> cons = null; // TODO 193 | 194 | List list = new ArrayList<>(List.of("a", "b", "c")); 195 | cons.accept(list); 196 | assertTrue(list.isEmpty()); 197 | } 198 | 199 | /** 200 | * Write an unbound method reference that clears the given list. 201 | */ 202 | @Test @Ignore 203 | public void c_consumer3() { 204 | Consumer> cons = null; // TODO 205 | 206 | List list = new ArrayList<>(List.of("a", "b", "c")); 207 | cons.accept(list); 208 | assertTrue(list.isEmpty()); 209 | } 210 | 211 | /** 212 | * Given two consumers, create a consumer that passes the String to the 213 | * first consumer, then to the second. 214 | */ 215 | @Test @Ignore 216 | public void c_consumer4() { 217 | Consumer> c1 = list -> list.add("first"); 218 | Consumer> c2 = list -> list.add("second"); 219 | 220 | Consumer> consumer = null; // TODO 221 | 222 | List list = new ArrayList<>(List.of("a", "b", "c")); 223 | consumer.accept(list); 224 | assertEquals(List.of("a", "b", "c", "first", "second"), list); 225 | } 226 | // Hint: 227 | // 228 | // see java.util.function.Consumer.andThen() 229 | // 230 | 231 | /** 232 | * Write a lambda expression that returns a new StringBuilder 233 | * containing the string "abc". 234 | */ 235 | @Test @Ignore 236 | public void d_supplier1() { 237 | Supplier sup = null; // TODO 238 | 239 | assertEquals("abc", sup.get().toString()); 240 | } 241 | 242 | /** 243 | * Write a lambda expression that returns a new, empty StringBuilder. 244 | */ 245 | @Test @Ignore 246 | public void d_supplier2() { 247 | Supplier sup = null; // TODO 248 | 249 | assertEquals("", sup.get().toString()); 250 | } 251 | 252 | /** 253 | * Write a constructor reference that returns a new, empty StringBuilder. 254 | */ 255 | @Test @Ignore 256 | public void d_supplier3() { 257 | Supplier sup = null; // TODO 258 | 259 | assertEquals("", sup.get().toString()); 260 | } 261 | 262 | /** 263 | * Write a lambda expression that, given two strings, returns the result 264 | * of concatenating the first with the second, followed by the 265 | * first again. 266 | */ 267 | @Test @Ignore 268 | public void e_bifunction1() { 269 | BiFunction bifunc = null; // TODO 270 | 271 | assertEquals("FirstSecondFirst", bifunc.apply("First", "Second")); 272 | } 273 | 274 | /** 275 | * Write a lambda expression that returns the index of 276 | * the first occurrence of the second string within the first string, 277 | * or -1 if the second string doesn't occur within the first string. 278 | */ 279 | @Test @Ignore 280 | public void e_bifunction2() { 281 | BiFunction bifunc = null; // TODO 282 | 283 | assertEquals(3, bifunc.apply("abcdefghi", "def").intValue()); 284 | assertEquals(-1, bifunc.apply("abcdefghi", "xyz").intValue()); 285 | } 286 | // Hint: 287 | // 288 | // The String method 289 | // public int indexOf(String) 290 | // works as a BiFunction, because the receiver (a String instance) 291 | // counts as the first argument. The argument to indexOf() becomes 292 | // the second argument to the BiFunction. 293 | // 294 | 295 | /** 296 | * Write an unbound method reference that returns the index of 297 | * the first occurrence of the second string within the first string, 298 | * or -1 if the second string doesn't occur within the first string. 299 | */ 300 | @Test @Ignore 301 | public void e_bifunction3() { 302 | BiFunction bifunc = null; // TODO 303 | 304 | assertEquals(3, bifunc.apply("abcdefghij", "def").intValue()); 305 | assertEquals(-1, bifunc.apply("abcdefghij", "xyz").intValue()); 306 | } 307 | // Hint 1: 308 | // 309 | // Try using the IDE command to convert the lambda from the previous 310 | // exercise into a method reference. 311 | // 312 | // Hint 2: 313 | // 314 | // This is just like the example above with the argument shifting. 315 | // The only difference is that arguments aren't specified in a 316 | // method reference, so overload resolution has to do more work 317 | // to find the overloaded method that matches. 318 | // 319 | 320 | 321 | /** 322 | * Write a lambda expression that appends the 'suffix' 323 | * variable (a String) to the sb variable (a StringBuilder). 324 | */ 325 | @Test @Ignore 326 | public void f_runnable1() { 327 | StringBuilder sb = new StringBuilder("abc"); 328 | String suffix = "xyz"; 329 | 330 | Runnable r = null; // TODO 331 | 332 | r.run(); 333 | r.run(); 334 | r.run(); 335 | assertEquals("abcxyzxyzxyz", sb.toString()); 336 | } 337 | 338 | /** 339 | * Write a lambda expression that takes a string argument 340 | * and returns the index of that argument into the string 341 | * "abcdefghij", or that returns -1 if the string argument 342 | * doesn't occur. 343 | */ 344 | @Test @Ignore 345 | public void g_boundMethodRef1() { 346 | Function func = null; // TODO 347 | 348 | assertEquals(2, func.apply("cde").intValue()); 349 | assertEquals(4, func.apply("efg").intValue()); 350 | assertEquals(-1, func.apply("xyz").intValue()); 351 | } 352 | // Hint: 353 | // 354 | // Call the indexOf() method on a string literal. 355 | // 356 | 357 | /** 358 | * Write a bound method reference that takes a string argument 359 | * and returns the index of that argument into the string 360 | * "abcdefghij", or that returns -1 if the string argument 361 | * doesn't occur. A bound method reference has an instance, 362 | * or an expression that evaluates to an instance, on the left-hand 363 | * side of the :: operator: 364 | * 365 | * myObject::methodname 366 | * 367 | * This is in contrast to an unbound method reference, which has 368 | * a classname on the left-hand side of the :: operator. 369 | */ 370 | @Test @Ignore 371 | public void g_boundMethodRef2() { 372 | Function func = null; // TODO 373 | 374 | assertEquals(2, func.apply("cde").intValue()); 375 | assertEquals(4, func.apply("efg").intValue()); 376 | assertEquals(-1, func.apply("xyz").intValue()); 377 | } 378 | // Hint: 379 | // 380 | // Place a string literal on the left-hand side of the :: operator. 381 | // 382 | } 383 | -------------------------------------------------------------------------------- /LambdaLab/test/exercises/B_Comparators.java: -------------------------------------------------------------------------------- 1 | package exercises; 2 | 3 | import java.util.Comparator; 4 | import java.util.function.IntBinaryOperator; 5 | 6 | import org.junit.Ignore; 7 | import org.junit.Test; 8 | 9 | import static org.junit.Assert.assertEquals; 10 | import static org.junit.Assert.assertFalse; 11 | import static org.junit.Assert.assertTrue; 12 | 13 | import model.Person; 14 | 15 | /** 16 | * Exercises to create comparators using lambda expressions 17 | * and using the Comparator combinators. Some of the exercises 18 | * use a Person class, which is a simple POJO containing a last 19 | * name, first name, and age, with the obvious constructors and 20 | * getters. 21 | */ 22 | public class B_Comparators { 23 | 24 | final Person michael = new Person("Michael", "Jackson", 51); 25 | final Person rod = new Person("Rod", "Stewart", 71); 26 | final Person paul = new Person("Paul", "McCartney", 74); 27 | final Person mick = new Person("Mick", "Jagger", 73); 28 | final Person jermaine = new Person("Jermaine", "Jackson", 61); 29 | 30 | /** 31 | * Write a Comparator that compare instances of String using their length. 32 | * For instance FOUR (4 letters) is greater than TWO (three letters) 33 | */ 34 | @Test @Ignore 35 | public void comparator01() { 36 | Comparator compareByLength = null; // TODO 37 | 38 | assertTrue(compareByLength.compare("FOUR", "TWO") > 0); 39 | assertTrue(compareByLength.compare("ONE", "SEVEN") < 0); 40 | assertTrue(compareByLength.compare("ONE", "TWO") == 0); 41 | } 42 | // Hint: 43 | // 44 | // Check the static factory methods of the Comparator interface. Remember 45 | // how you implemented functions in the previous exercises. Write it using 46 | // a method reference. 47 | // 48 | 49 | /** 50 | * Write a Comparator that compare instances of String using their length. 51 | * If the lengths are the same, then use the alphabetical order. 52 | */ 53 | @Test @Ignore 54 | public void comparator02() { 55 | Comparator compareByLengthThenAlphabetical = null; // TODO 56 | 57 | assertTrue(compareByLengthThenAlphabetical.compare("FOUR", "TWO") > 0); 58 | assertTrue(compareByLengthThenAlphabetical.compare("ONE", "SEVEN") < 0); 59 | assertTrue(compareByLengthThenAlphabetical.compare("ONE", "TWO") < 0); 60 | assertTrue(compareByLengthThenAlphabetical.compare("FOUR", "FIVE") > 0); 61 | assertTrue(compareByLengthThenAlphabetical.compare("EIGHT", "EIGHT") == 0); 62 | } 63 | // Hint: 64 | // 65 | // Use the previous comparator and check the default methods of the 66 | // Comparator interface. 67 | // Check also the factory methods of the Comparator interface, and remember 68 | // that String is comparable. 69 | // 70 | 71 | /** 72 | * Write a Comparator that compares instances of Person using their lastName. 73 | */ 74 | @Test @Ignore 75 | public void comparator03() { 76 | Comparator comparebyLastName = null; // TODO 77 | 78 | assertTrue(comparebyLastName.compare(michael, rod) < 0); 79 | assertTrue(comparebyLastName.compare(paul, paul) == 0); 80 | assertTrue(comparebyLastName.compare(michael, jermaine) == 0); 81 | } 82 | // Hint: 83 | // 84 | // Check the static factory methods of the Comparator interface. Remember 85 | // how you implemented functions in the previous exercises. Write it using 86 | // a method reference. 87 | // 88 | 89 | /** 90 | * Write a Comparator that compares instances of Person using their 91 | * lastName, and if their last name is the same, uses their first name. 92 | */ 93 | @Test @Ignore 94 | public void comparator04() { 95 | Comparator comparebyLastNameThenFirstName = null; // TODO 96 | 97 | assertTrue(comparebyLastNameThenFirstName.compare(michael, rod) < 0); 98 | assertTrue(comparebyLastNameThenFirstName.compare(paul, paul) == 0); 99 | assertTrue(comparebyLastNameThenFirstName.compare(michael, jermaine) > 0); 100 | } 101 | // Hint: 102 | // 103 | // Use the previous comparator and check the default methods of the Comparator interface. 104 | // 105 | 106 | /** 107 | * Write a Comparator that compares the people in the order reversed from 108 | * the one you wrote in the comparator04() exercise. That is, the person 109 | * with the greater last name should be ordered first. If two persons have 110 | * the same last name, the one with the greater first name should be 111 | * ordered first. 112 | */ 113 | @Test @Ignore 114 | public void comparator05() { 115 | Comparator comparebyLastNameThenFirstNameReversed = null; // TODO 116 | 117 | assertFalse(comparebyLastNameThenFirstNameReversed.compare(michael, rod) < 0); 118 | assertTrue(comparebyLastNameThenFirstNameReversed.compare(paul, paul) == 0); 119 | assertFalse(comparebyLastNameThenFirstNameReversed.compare(michael, jermaine) > 0); 120 | } 121 | // Hint: 122 | // 123 | // Use the previous comparator and check the default methods of the Comparator interface. 124 | // 125 | 126 | /** 127 | * Write a Comparator that compares the people in the same order than the 128 | * one you wrote in comparator04(), but that supports null values. The null 129 | * values should be considered greater than any non-null values. 130 | */ 131 | @Test @Ignore 132 | public void comparator06() { 133 | Comparator comparebyLastNameThenFirstNameWithNull = null; // TODO 134 | 135 | assertTrue(comparebyLastNameThenFirstNameWithNull.compare(michael, rod) < 0); 136 | assertTrue(comparebyLastNameThenFirstNameWithNull.compare(paul, paul) == 0); 137 | assertTrue(comparebyLastNameThenFirstNameWithNull.compare(michael, jermaine) > 0); 138 | assertTrue(comparebyLastNameThenFirstNameWithNull.compare(mick, null) < 0); 139 | assertTrue(comparebyLastNameThenFirstNameWithNull.compare(null, mick) > 0); 140 | } 141 | // Hint: 142 | // 143 | // Use the previous comparator and check the static methods of the Comparator interface. 144 | // 145 | 146 | /** 147 | * Write a Comparator that compares two people by age. 148 | * Try to write the comparator so as to avoid boxing of primitives. 149 | */ 150 | @Test @Ignore 151 | public void comparator07() { 152 | Comparator comparebyAge = null; // TODO 153 | 154 | assertTrue(comparebyAge.compare(michael, rod) < 0); 155 | assertTrue(comparebyAge.compare(paul, paul) == 0); 156 | assertTrue(comparebyAge.compare(mick, jermaine) > 0); 157 | } 158 | // Hint: 159 | // 160 | // Look for static methods on the Comparator interface that 161 | // have primitive specializations. 162 | // 163 | 164 | /** 165 | * Write a lambda expression that compares two int values and returns an 166 | * int result that is less than, equal to, or greater than zero, like 167 | * a comparator. Watch out for overflow. The Comparator interface takes 168 | * two objects, but in this case we are comparing int primitives, so the 169 | * functional interface we use is IntBinaryOperator. 170 | */ 171 | @Test @Ignore 172 | public void comparator08() { 173 | IntBinaryOperator intCompare = null; // TODO 174 | 175 | assertTrue(intCompare.applyAsInt(0, 1) < 0); 176 | assertTrue(intCompare.applyAsInt(1, 1) == 0); 177 | assertTrue(intCompare.applyAsInt(2, 1) > 0); 178 | assertTrue(intCompare.applyAsInt(Integer.MIN_VALUE, Integer.MAX_VALUE) < 0); 179 | assertTrue(intCompare.applyAsInt(Integer.MAX_VALUE, Integer.MIN_VALUE) > 0); 180 | } 181 | // Hint: 182 | // 183 | // Use a ternary operator (cond ? result1 : result2) instead of subtraction. 184 | // 185 | 186 | /** 187 | * Write a method reference that compares two int values and returns an 188 | * int result that is less than, equal to, or greater than zero, like 189 | * a comparator. 190 | */ 191 | @Test @Ignore 192 | public void comparator09() { 193 | IntBinaryOperator intCompare = null; // TODO 194 | 195 | assertTrue(intCompare.applyAsInt(0, 1) < 0); 196 | assertTrue(intCompare.applyAsInt(1, 1) == 0); 197 | assertTrue(intCompare.applyAsInt(2, 1) > 0); 198 | assertTrue(intCompare.applyAsInt(Integer.MIN_VALUE, Integer.MAX_VALUE) < 0); 199 | assertTrue(intCompare.applyAsInt(Integer.MAX_VALUE, Integer.MIN_VALUE) > 0); 200 | } 201 | // Hint: 202 | // 203 | // Use a method reference to a static method on the Integer class. 204 | // 205 | 206 | interface DoubleToIntBiFunction { 207 | int applyAsInt(double a, double b); 208 | } 209 | 210 | /** 211 | * Write a method reference that compares two double values and returns an 212 | * int result that is less than, equal to, or greater than zero, like 213 | * a comparator. There functional interface that takes two doubles and returns 214 | * an int, so we define one here. Comparing double values introduces 215 | * special cases such NaN. Consider all NaN values to be equal to each other 216 | * and greater than any non-NaN value. 217 | */ 218 | @Test @Ignore 219 | public void comparator10() { 220 | DoubleToIntBiFunction doubleCompare = null; // TODO 221 | 222 | assertTrue(doubleCompare.applyAsInt(0.0, 1.0) < 0); 223 | assertTrue(doubleCompare.applyAsInt(1.0, 1.0) == 0); 224 | assertTrue(doubleCompare.applyAsInt(2.0, 1.0) > 0); 225 | assertTrue(doubleCompare.applyAsInt(Double.NaN, Double.NaN) == 0); 226 | assertTrue(doubleCompare.applyAsInt(Double.NaN, 0.0) > 0); 227 | assertTrue(doubleCompare.applyAsInt(0.0, Double.NaN) < 0); 228 | } 229 | // Hint: 230 | // 231 | // Use a method reference to a static method on the Double class. 232 | // 233 | } 234 | -------------------------------------------------------------------------------- /LambdaLab/test/exercises/C_DefaultMethods.java: -------------------------------------------------------------------------------- 1 | package exercises; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.TreeMap; 9 | import java.util.stream.Collectors; 10 | 11 | import org.junit.Ignore; 12 | import org.junit.Test; 13 | 14 | import static org.junit.Assert.assertEquals; 15 | import static org.junit.Assert.assertFalse; 16 | import static org.junit.Assert.assertTrue; 17 | 18 | /** 19 | * This set of exercises covers new default methods on 20 | * the Collections and related APIs. 21 | */ 22 | public class C_DefaultMethods { 23 | 24 | /** 25 | * Given a list of StringBuilders, modify each StringBuilder 26 | * in-place by appending the string "new" to each one. 27 | */ 28 | @Test @Ignore 29 | public void c01_appendNew() { 30 | List sbList = List.of( 31 | new StringBuilder("alfa"), 32 | new StringBuilder("bravo"), 33 | new StringBuilder("charlie")); 34 | 35 | // TODO write code to modify sbList 36 | 37 | assertEquals(List.of("alfanew", "bravonew", "charlienew"), 38 | sbList.stream() 39 | .map(StringBuilder::toString) 40 | .collect(Collectors.toList())); 41 | } 42 | // Hint: 43 | // 44 | // Use Iterable.forEach(). 45 | // 46 | 47 | 48 | /** 49 | * Remove the words that have odd lengths from the list. 50 | */ 51 | @Test @Ignore 52 | public void c02_removeOddLengthWords() { 53 | List list = new ArrayList<>(Arrays.asList( 54 | "alfa", "bravo", "charlie", "delta", "echo", "foxtrot")); 55 | 56 | // TODO write code to modify list 57 | 58 | assertEquals(List.of("alfa", "echo"), list); 59 | } 60 | // Hint: 61 | // 62 | // Use Collection.removeIf(). 63 | // 64 | 65 | 66 | /** 67 | * Replace every word in the list with its upper case equivalent. 68 | */ 69 | @Test @Ignore 70 | public void c03_upcaseAllWords() { 71 | List list = Arrays.asList( 72 | "alfa", "bravo", "charlie", "delta", "echo", "foxtrot"); 73 | 74 | // TODO code to modify list 75 | 76 | assertEquals(List.of("ALFA", "BRAVO", "CHARLIE", "DELTA", "ECHO", "FOXTROT"), 77 | list); 78 | } 79 | // Hint: 80 | // 81 | // Use List.replaceAll(). 82 | // 83 | 84 | 85 | /** 86 | * Given a map whose keys are Integers and whose values are StringBuilders, 87 | * append to each StringBuilder the string representation of its corresponding 88 | * Integer key. This should mutate each StringBuilder value in-place. 89 | */ 90 | @Test @Ignore 91 | public void c04_appendToMapValues() { 92 | Map map = new TreeMap<>(); 93 | map.put(1, new StringBuilder("alfa")); 94 | map.put(2, new StringBuilder("bravo")); 95 | map.put(3, new StringBuilder("charlie")); 96 | 97 | // TODO write code to modify map 98 | 99 | assertEquals(3, map.size()); 100 | assertTrue(map.values().stream().allMatch(x -> x instanceof StringBuilder)); 101 | assertEquals("alfa1", map.get(1).toString()); 102 | assertEquals("bravo2", map.get(2).toString()); 103 | assertEquals("charlie3", map.get(3).toString()); 104 | } 105 | // Hint: 106 | // 107 | // Use Map.forEach(). 108 | // 109 | 110 | 111 | /** 112 | * Given a map whose keys are Integers and whose values are Strings, 113 | * append to each String the string representation of its corresponding 114 | * Integer key. 115 | */ 116 | @Test @Ignore 117 | public void c05_replaceMapValues() { 118 | Map map = new TreeMap<>(); 119 | map.put(1, "alfa"); 120 | map.put(2, "bravo"); 121 | map.put(3, "charlie"); 122 | 123 | // TODO write code to modify map 124 | 125 | assertEquals(Map.of(1, "alfa1", 126 | 2, "bravo2", 127 | 3, "charlie3"), 128 | map); 129 | } 130 | // Hint: 131 | // 132 | // Use Map.replaceAll(). 133 | // 134 | 135 | 136 | /** 137 | * Given a list of words, populate a map whose keys are the lengths of 138 | * each word, and whose values are list of words with that length. 139 | */ 140 | @Test @Ignore 141 | public void c06_mapOfListOfStringsByLength() { 142 | List list = List.of( 143 | "aardvark", "bison", "capybara", 144 | "alligator", "bushbaby", "chimpanzee", 145 | "avocet", "bustard", "capuchin"); 146 | Map> result = new TreeMap<>(); 147 | 148 | // TODO write code to populate result 149 | 150 | assertEquals(Map.of( 5, List.of("bison"), 151 | 6, List.of("avocet"), 152 | 7, List.of("bustard"), 153 | 8, List.of("aardvark", "capybara", "bushbaby", "capuchin"), 154 | 9, List.of("alligator"), 155 | 10, List.of("chimpanzee")), 156 | result); 157 | } 158 | // 159 | // Use Map.computeIfAbsent() within Iterable.forEach(). 160 | // 161 | 162 | /** 163 | * Given a list of words, populate a map whose keys are the initial characters of 164 | * each word, and whose values are the concatenation of the words with that 165 | * initial character. When concatenating the words, they should be 166 | * separated by a colon (':'). 167 | */ 168 | @Test @Ignore 169 | public void c07_mapOfStringByInitialCharacter() { 170 | List list = List.of( 171 | "aardvark", "bison", "capybara", 172 | "alligator", "bushbaby", "chimpanzee", 173 | "avocet", "bustard", "capuchin"); 174 | Map result = new TreeMap<>(); 175 | 176 | // TODO write code to populate result 177 | 178 | assertEquals(Map.of('a', "aardvark:alligator:avocet", 179 | 'b', "bison:bushbaby:bustard", 180 | 'c', "capybara:chimpanzee:capuchin"), 181 | result); 182 | } 183 | // Hint: 184 | // 185 | // Use Map.merge() within Iterable.forEach(). 186 | // 187 | 188 | 189 | /** 190 | * For some reason the provided map doesn't have mappings for all the keys. This 191 | * is a problem, because if we call get() on a key that isn't present, it returns 192 | * null, and we need to add checks to protect against NullPointerException. 193 | * Write code to ensure that all missing keys are mapped to the empty string. 194 | */ 195 | @Test @Ignore 196 | public void c08_mapWithMissingValues() { 197 | List keys = Arrays.asList("a", "b", "c", "d", "e", "f", "g"); 198 | Map map = new HashMap<>(Map.of("a", "alfa", 199 | "b", "bravo", 200 | "c", "charlie", 201 | "d", "delta")); 202 | 203 | // TODO write code to fix the map 204 | 205 | assertEquals(Map.of("a", "alfa", 206 | "b", "bravo", 207 | "c", "charlie", 208 | "d", "delta", 209 | "e", "", 210 | "f", "", 211 | "g", ""), 212 | map); 213 | } 214 | // Hint: 215 | // 216 | // Check the Map.putIfAbsent() default method. 217 | // 218 | 219 | 220 | /** 221 | * In the previous example, we added map entries that had a default value. 222 | * We've now determined that's incorrect, and we want to undo that. This 223 | * time, we want to remove the entry if the value is the empty string. 224 | */ 225 | @Test @Ignore 226 | public void c09_mapRemoveEntriesWithEmptyValues() { 227 | List keys = Arrays.asList("a", "b", "c", "d", "e", "f", "g"); 228 | Map map = new HashMap<>(Map.of("a", "alfa", 229 | "b", "bravo", 230 | "c", "charlie", 231 | "d", "delta", 232 | "e", "", 233 | "f", "", 234 | "g", "")); 235 | 236 | // TODO write code to fix the map 237 | 238 | assertEquals(Map.of("a", "alfa", 239 | "b", "bravo", 240 | "c", "charlie", 241 | "d", "delta"), 242 | map); 243 | } 244 | // Hint: 245 | // 246 | // Check the two-arg Map.remove() default method. 247 | // 248 | 249 | 250 | /** 251 | * We need another way to deal with the problem of the previous example. 252 | * Instead of removing entries whose value is the empty string, we want 253 | * to replace the empty-string values with a value that's the key itself. 254 | * Write the code to do that. 255 | */ 256 | @Test @Ignore 257 | public void c10_mapReplaceEmptyValues() { 258 | List keys = Arrays.asList("a", "b", "c", "d", "e", "f", "g"); 259 | Map map = new HashMap<>(Map.of("a", "alfa", 260 | "b", "bravo", 261 | "c", "charlie", 262 | "d", "delta", 263 | "e", "", 264 | "f", "", 265 | "g", "")); 266 | 267 | // TODO write code to fix the map 268 | 269 | assertEquals(Map.of("a", "alfa", 270 | "b", "bravo", 271 | "c", "charlie", 272 | "d", "delta", 273 | "e", "e", 274 | "f", "f", 275 | "g", "g"), 276 | map); 277 | } 278 | // Hint: 279 | // 280 | // Check the Map.replace() default method that takes 3 arguments. 281 | // 282 | 283 | 284 | /** 285 | * We are still dealing with a map with missing entries. For entries that 286 | * are present, we want to convert the value to upper case; and for keys 287 | * that are not present, we want to add an entry where the value is the 288 | * same as the key. 289 | */ 290 | @Test @Ignore 291 | public void c11_computeWithMissingEntries() { 292 | List keys = Arrays.asList("a", "b", "c", "d", "e", "f", "g"); 293 | Map map = new HashMap<>(Map.of("a", "alfa", 294 | "b", "bravo", 295 | "c", "charlie", 296 | "d", "delta")); 297 | 298 | // TODO write code transform the map 299 | 300 | assertEquals(Map.of("a", "ALFA", 301 | "b", "BRAVO", 302 | "c", "CHARLIE", 303 | "d", "DELTA", 304 | "e", "e", 305 | "f", "f", 306 | "g", "g"), 307 | map); 308 | } 309 | // Hint: 310 | // 311 | // Check the Map.compute() default method, and read the Javadoc carefully 312 | // regarding the mappings that aren't present. 313 | // 314 | 315 | 316 | /** 317 | * The map now has several entries, some with valid values, and some 318 | * with values that are the empty string. This time, we want to convert 319 | * the non-empty values to upper case, but we want to remove the entries 320 | * for which the values are the empty string. 321 | */ 322 | @Test @Ignore 323 | public void c12_computeAndRemoveSomeEntries() { 324 | List keys = Arrays.asList("a", "b", "c", "d", "e", "f", "g"); 325 | Map map = new HashMap<>(Map.of("a", "alfa", 326 | "b", "bravo", 327 | "c", "charlie", 328 | "d", "delta", 329 | "e", "", 330 | "f", "", 331 | "g", "")); 332 | 333 | // TODO write code transform the map 334 | 335 | assertEquals(Map.of("a", "ALFA", 336 | "b", "BRAVO", 337 | "c", "CHARLIE", 338 | "d", "DELTA"), 339 | map); 340 | } 341 | // Hint: 342 | // 343 | // Check the Map.compute() default method, read the Javadoc carefully 344 | // for the handling of null values returned from the function. 345 | // 346 | } 347 | -------------------------------------------------------------------------------- /LambdaLab/test/exercises/D_SimpleStreams.java: -------------------------------------------------------------------------------- 1 | package exercises; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.nio.charset.StandardCharsets; 6 | import java.nio.file.Files; 7 | import java.nio.file.Paths; 8 | import java.util.Arrays; 9 | import java.util.Comparator; 10 | import java.util.List; 11 | import java.util.stream.Collectors; 12 | import java.util.stream.IntStream; 13 | 14 | import org.junit.After; 15 | import org.junit.Before; 16 | import org.junit.Ignore; 17 | import org.junit.Test; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | import static org.junit.Assert.assertFalse; 21 | import static org.junit.Assert.assertTrue; 22 | 23 | /** 24 | * This set of exercises covers simple stream pipelines, 25 | * including intermediate operations and basic collectors. 26 | * 27 | * Some of these exercises use a BufferedReader variable 28 | * named "reader" that the test has set up for you. 29 | */ 30 | public class D_SimpleStreams { 31 | /** 32 | * Given a list of words, create an output list that contains 33 | * only the odd-length words, converted to upper case. 34 | */ 35 | @Test @Ignore 36 | public void d1_upcaseOddLengthWords() { 37 | List input = List.of( 38 | "alfa", "bravo", "charlie", "delta", "echo", "foxtrot"); 39 | 40 | List result = null; // TODO 41 | 42 | assertEquals(List.of("BRAVO", "CHARLIE", "DELTA", "FOXTROT"), result); 43 | } 44 | // Hint 1: 45 | // 46 | // Use filter() and map(). 47 | // 48 | // Hint 2: 49 | // 50 | // To create the result list, use collect() with one of the 51 | // predefined collectors on the Collectors class. 52 | // 53 | 54 | 55 | /** 56 | * Take the third through fifth words of the list, extract the 57 | * second letter from each, and join them, separated by commas, 58 | * into a single string. Watch for off-by-one errors. 59 | */ 60 | @Test @Ignore 61 | public void d2_joinStreamRange() { 62 | List input = List.of( 63 | "alfa", "bravo", "charlie", "delta", "echo", "foxtrot"); 64 | 65 | String result = ""; // TODO 66 | 67 | assertEquals("h,e,c", result); 68 | } 69 | // Hint 1: 70 | // 71 | // Use Stream.skip() and Stream.limit(). 72 | // 73 | // Hint 2: 74 | // 75 | // Use Collectors.joining(). 76 | // 77 | 78 | 79 | /** 80 | * Count the number of lines in the text file. (Remember to 81 | * use the BufferedReader named "reader" that has already been 82 | * opened for you.) 83 | * 84 | * @throws IOException 85 | */ 86 | @Test @Ignore 87 | public void d3_countLinesInFile() throws IOException { 88 | long count = 0; // TODO 89 | 90 | assertEquals(14, count); 91 | } 92 | // Hint 1: 93 | // 94 | // Use BufferedReader.lines() to get a stream of lines. 95 | // 96 | // Hint 2: 97 | // 98 | // Use Stream.count(). 99 | // 100 | 101 | 102 | /** 103 | * Find the length of the longest line in the text file. 104 | * 105 | * @throws IOException 106 | */ 107 | @Test @Ignore 108 | public void d4_findLengthOfLongestLine() throws IOException { 109 | int longestLength = 0; // TODO 110 | 111 | assertEquals(53, longestLength); 112 | } 113 | // Hint 1: 114 | // 115 | // Use Stream.mapToInt() to convert a stream of objects to an IntStream. 116 | // 117 | // Hint 2: 118 | // 119 | // Look at java.util.OptionalInt to get the result. 120 | // 121 | // Hint 3: 122 | // 123 | // Think about the case where the OptionalInt might be empty 124 | // (that is, where it has no value). 125 | // 126 | 127 | 128 | /** 129 | * Find the longest line in the text file. 130 | * 131 | * @throws IOException 132 | */ 133 | @Test @Ignore 134 | public void d5_findLongestLine() throws IOException { 135 | String longest = null; // TODO 136 | 137 | assertEquals("Feed'st thy light's flame with self-substantial fuel,", longest); 138 | } 139 | // Hint 1: 140 | // 141 | // Use Stream.max() with a Comparator. 142 | // 143 | // Hint 2: 144 | // 145 | // Use static methods on Comparator to help create a Comparator instance. 146 | // 147 | 148 | 149 | /** 150 | * Select the longest words from the input list. That is, select the words 151 | * whose lengths are equal to the maximum word length. 152 | */ 153 | @Test @Ignore 154 | public void d6_selectLongestWords() { 155 | List input = List.of( 156 | "alfa", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel"); 157 | 158 | List result = null; // TODO 159 | 160 | assertEquals(List.of("charlie", "foxtrot"), result); 161 | } 162 | // Hint: 163 | // 164 | // Consider making two passes over the input stream. 165 | // 166 | 167 | /** 168 | * Select the list of words from the input list whose length is greater than 169 | * the word's position in the list (starting from zero) . 170 | */ 171 | @Test @Ignore 172 | public void d7_selectByLengthAndPosition() { 173 | List input = List.of( 174 | "alfa", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel"); 175 | 176 | List result = null; // TODO 177 | 178 | assertEquals(List.of("alfa", "bravo", "charlie", "delta", "foxtrot"), result); 179 | } 180 | // Hint: 181 | // 182 | // Instead of a stream of words (Strings), run an IntStream of indexes of 183 | // the input list, using index values to get elements from the input list. 184 | // 185 | 186 | 187 | // ======================================================== 188 | // END OF EXERCISES 189 | // TEST INFRASTRUCTURE IS BELOW 190 | // ======================================================== 191 | 192 | 193 | private BufferedReader reader; 194 | 195 | @Before 196 | public void z_setUpBufferedReader() throws IOException { 197 | reader = Files.newBufferedReader( 198 | Paths.get("SonnetI.txt"), StandardCharsets.UTF_8); 199 | } 200 | 201 | @After 202 | public void z_closeBufferedReader() throws IOException { 203 | reader.close(); 204 | } 205 | 206 | } 207 | -------------------------------------------------------------------------------- /LambdaLab/test/exercises/E_IntermediateStreams.java: -------------------------------------------------------------------------------- 1 | package exercises; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.math.BigInteger; 6 | import java.nio.charset.StandardCharsets; 7 | import java.nio.file.Files; 8 | import java.nio.file.Paths; 9 | import java.util.AbstractCollection; 10 | import java.util.AbstractList; 11 | import java.util.ArrayList; 12 | import java.util.Arrays; 13 | import java.util.Comparator; 14 | import java.util.List; 15 | import java.util.Objects; 16 | import java.util.Random; 17 | import java.util.regex.Pattern; 18 | import java.util.stream.Collectors; 19 | import java.util.stream.LongStream; 20 | import java.util.stream.Stream; 21 | 22 | import org.junit.After; 23 | import org.junit.Before; 24 | import org.junit.Ignore; 25 | import org.junit.Test; 26 | 27 | import static org.junit.Assert.assertEquals; 28 | import static org.junit.Assert.assertFalse; 29 | import static org.junit.Assert.assertTrue; 30 | 31 | /** 32 | * This set of exercises covers more advanced stream operations 33 | * longer stream pipelines, and simple reductions. 34 | */ 35 | public class E_IntermediateStreams { 36 | 37 | /** 38 | * Convert a list of strings into a list of characters. 39 | */ 40 | @Test @Ignore 41 | public void e1_stringsToCharacters() { 42 | List input = List.of("alfa", "bravo", "charlie"); 43 | 44 | List result = null; // TODO 45 | 46 | assertEquals("[a, l, f, a, b, r, a, v, o, c, h, a, r, l, i, e]", result.toString()); 47 | assertTrue(result.stream().allMatch(x -> x instanceof Character)); 48 | } 49 | // Hint 1: 50 | // 51 | // Use String.chars() and Stream.flatMap(). 52 | // 53 | // Hint 2: 54 | // 55 | // Pay attention to the return type of String.chars() and boxing conversion. 56 | // 57 | 58 | 59 | /** 60 | * Collect all the words from the text file into a list. 61 | * Use the regular expression Pattern SPLIT_PATTERN to split 62 | * a string into words, and use Pattern.splitAsStream(String) 63 | * to do the splitting. SPLIT_PATTERN is defined at the bottom 64 | * of this file. As before, use the BufferedReader variable 65 | * named "reader" that has been set up for you to read from 66 | * the text file. 67 | * 68 | * @throws IOException 69 | */ 70 | @Test @Ignore 71 | public void e2_listOfAllWords() throws IOException { 72 | List output = null; // TODO 73 | 74 | assertEquals( 75 | List.of( 76 | "From", "fairest", "creatures", "we", "desire", "increase", 77 | "That", "thereby", "beauty's", "rose", "might", "never", "die", 78 | "But", "as", "the", "riper", "should", "by", "time", "decease", 79 | "His", "tender", "heir", "might", "bear", "his", "memory", 80 | "But", "thou", "contracted", "to", "thine", "own", "bright", "eyes", 81 | "Feed'st", "thy", "light's", "flame", "with", "self", "substantial", "fuel", 82 | "Making", "a", "famine", "where", "abundance", "lies", 83 | "Thy", "self", "thy", "foe", "to", "thy", "sweet", "self", "too", "cruel", 84 | "Thou", "that", "art", "now", "the", "world's", "fresh", "ornament", 85 | "And", "only", "herald", "to", "the", "gaudy", "spring", 86 | "Within", "thine", "own", "bud", "buriest", "thy", "content", 87 | "And", "tender", "churl", "mak'st", "waste", "in", "niggarding", 88 | "Pity", "the", "world", "or", "else", "this", "glutton", "be", 89 | "To", "eat", "the", "world's", "due", "by", "the", "grave", "and", "thee"), 90 | output); 91 | } 92 | // Hint: 93 | // 94 | // Use Stream.flatMap(). 95 | // 96 | 97 | 98 | /** 99 | * Read the words from the text file, and create a list containing the words 100 | * of length 8 or longer, converted to lower case, and sorted alphabetically. 101 | * 102 | * @throws IOException 103 | */ 104 | @Test @Ignore 105 | public void e3_longLowerCaseSortedWords() throws IOException { 106 | List output = null; // TODO 107 | 108 | assertEquals( 109 | List.of( 110 | "abundance", "beauty's", "contracted", "creatures", 111 | "increase", "niggarding", "ornament", "substantial"), 112 | output); 113 | } 114 | // Hint: 115 | // 116 | // Use Stream.sorted(). 117 | // 118 | 119 | 120 | /** 121 | * Read the words from the text file, and create a list containing the words 122 | * of length 8 or longer, converted to lower case, and sorted reverse alphabetically. 123 | * (Same as above except for reversed sort order.) 124 | * 125 | * @throws IOException 126 | */ 127 | @Test @Ignore 128 | public void e4_longLowerCaseReverseSortedWords() throws IOException { 129 | List result = null; // TODO 130 | 131 | assertEquals( 132 | List.of( 133 | "substantial", "ornament", "niggarding", "increase", 134 | "creatures", "contracted", "beauty's", "abundance"), 135 | result); 136 | } 137 | // Hint: 138 | // 139 | // Use Comparator.reverseOrder(). 140 | // 141 | 142 | 143 | /** 144 | * Read words from the text file, and sort unique, lower-cased words by length, 145 | * then alphabetically within length, and place the result into an output list. 146 | * 147 | * @throws IOException 148 | */ 149 | @Test @Ignore 150 | public void e5_sortedLowerCaseDistinctByLengthThenAlphabetically() throws IOException { 151 | List result = null; // TODO 152 | 153 | assertEquals( 154 | List.of( 155 | "a", "as", "be", "by", "in", "or", "to", "we", 156 | "and", "art", "bud", "but", "die", "due", "eat", "foe", 157 | "his", "now", "own", "the", "thy", "too", "bear", "else", 158 | "eyes", "from", "fuel", "heir", "lies", "only", 159 | "pity", "rose", "self", "that", "thee", "this", "thou", 160 | "time", "with", "churl", "cruel", "flame", "fresh", "gaudy", 161 | "grave", "might", "never", "riper", "sweet", "thine", 162 | "waste", "where", "world", "bright", "desire", "famine", 163 | "herald", "mak'st", "making", "memory", "should", "spring", 164 | "tender", "within", "buriest", "content", "decease", 165 | "fairest", "feed'st", "glutton", "light's", "thereby", "world's", "beauty's", 166 | "increase", "ornament", "abundance", "creatures", "contracted", "niggarding", 167 | "substantial"), 168 | result); 169 | } 170 | // Hint 1: 171 | // 172 | // Use Stream.distinct(). 173 | // 174 | // Hint 2: 175 | // 176 | // Use Comparator.thenComparing(). 177 | // 178 | 179 | /** 180 | * Compute the value of 21!, that is, 21 factorial. This value is larger than 181 | * Long.MAX_VALUE, so you must use BigInteger. 182 | */ 183 | @Test @Ignore 184 | public void e6_bigFactorial() { 185 | BigInteger result = BigInteger.ONE; // TODO 186 | 187 | assertEquals(new BigInteger("51090942171709440000"), result); 188 | } 189 | // Hint 1: 190 | // 191 | // Use one of the range methods of LongStream to help create 192 | // the BigInteger instances. 193 | // 194 | // Hint 2: 195 | // 196 | // Use Stream.reduce() to "collapse" all elements of a stream into 197 | // a single value. 198 | // 199 | 200 | 201 | /** 202 | * Get the last word in the text file. 203 | * 204 | * @throws IOException 205 | */ 206 | @Test @Ignore 207 | public void e7_getLastWord() throws IOException { 208 | String result = null; // TODO 209 | 210 | assertEquals("thee", result); 211 | } 212 | // Hint: 213 | // 214 | // Use Stream.reduce() and think about the order of the arguments. 215 | // 216 | 217 | /** 218 | * Create a list containing ArrayList.class and all its super classes. 219 | */ 220 | @Test @Ignore 221 | public void e8_selectTheSuperClassesOfArrayList() { 222 | Class origin = ArrayList.class; 223 | 224 | List result = null; // TODO 225 | 226 | assertEquals( 227 | List.of(ArrayList.class, AbstractList.class, AbstractCollection.class, Object.class), 228 | result); 229 | } 230 | // Hint: 231 | // 232 | // There is a getSuperClass() method on the Class class. 233 | // Creating a stream of these classes can be made with Stream.iterate(). 234 | // Then you need to close that stream when the current class is null. 235 | // Java 9 added the takeWhile() method on the stream interface. 236 | // 237 | 238 | 239 | /** 240 | * Count the length of a stream dropping the first elements on a predicate. 241 | */ 242 | @Test @Ignore 243 | public void e9_countTheElementsAfterAPredicate() { 244 | 245 | Random rand = new Random(314L); 246 | Stream stream = Stream.iterate( 247 | "", 248 | (String s) -> { 249 | final int nextInt = rand.nextInt(10); 250 | return (nextInt == 0 && !s.isEmpty()) ? s.substring(0, s.length() - 1) : 251 | (nextInt == 8 || nextInt == 9) ? s + "+" 252 | : s; 253 | }).limit(100); 254 | 255 | long count = 0L; // TODO 256 | 257 | assertEquals(53, count); 258 | } 259 | // Hint: 260 | // 261 | // Java 9 added the dropWhile() method on the stream interface. 262 | // 263 | 264 | 265 | // ======================================================== 266 | // END OF EXERCISES 267 | // TEST INFRASTRUCTURE IS BELOW 268 | // ======================================================== 269 | 270 | 271 | // Pattern for splitting a string into words 272 | static final Pattern SPLIT_PATTERN = Pattern.compile("[- .:,]+"); 273 | 274 | private BufferedReader reader; 275 | 276 | @Before 277 | public void z_setUpBufferedReader() throws IOException { 278 | reader = Files.newBufferedReader( 279 | Paths.get("SonnetI.txt"), StandardCharsets.UTF_8); 280 | } 281 | 282 | @After 283 | public void z_closeBufferedReader() throws IOException { 284 | reader.close(); 285 | } 286 | 287 | } 288 | -------------------------------------------------------------------------------- /LambdaLab/test/exercises/F_AdvancedStreams.java: -------------------------------------------------------------------------------- 1 | package exercises; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.nio.charset.StandardCharsets; 6 | import java.nio.file.Files; 7 | import java.nio.file.Paths; 8 | import java.util.Arrays; 9 | import java.util.Collections; 10 | import java.util.HashSet; 11 | import java.util.IntSummaryStatistics; 12 | import java.util.List; 13 | import java.util.Map; 14 | import java.util.Random; 15 | import java.util.Set; 16 | import java.util.function.Function; 17 | import java.util.regex.Pattern; 18 | import java.util.stream.Collectors; 19 | import java.util.stream.IntStream; 20 | import java.util.stream.Stream; 21 | 22 | import org.junit.After; 23 | import org.junit.Before; 24 | import org.junit.Ignore; 25 | import org.junit.Test; 26 | 27 | import static java.util.Map.entry; 28 | import static org.junit.Assert.assertEquals; 29 | import static org.junit.Assert.assertFalse; 30 | import static org.junit.Assert.assertTrue; 31 | 32 | /** 33 | * This set of exercises covers advanced stream operations, 34 | * including grouping collectors, composition of collectors, 35 | * and customized collectors. 36 | */ 37 | public class F_AdvancedStreams { 38 | 39 | /** 40 | * Categorize the words from the text file into a map, where the map's key 41 | * is the length of each word, and the value corresponding to a key is a 42 | * list of words of that length. Don't bother with uniqueness or lower- 43 | * casing the words. As before, use the BufferedReader variable named 44 | * "reader" that has been set up for you to read from the text file, and 45 | * use SPLIT_PATTERN for splitting the line into words. 46 | * 47 | * @throws IOException 48 | */ 49 | @Test @Ignore 50 | public void f1_mapLengthToWordList() throws IOException { 51 | Map> result = null; // TODO 52 | 53 | assertEquals(10, result.get(7).size()); 54 | assertEquals(Set.of("beauty's", "increase", "ornament"), new HashSet<>(result.get(8))); 55 | assertEquals(Set.of("abundance", "creatures"), new HashSet<>(result.get(9))); 56 | assertEquals(Set.of("contracted", "niggarding"), new HashSet<>(result.get(10))); 57 | assertEquals(List.of("substantial"), result.get(11)); 58 | assertFalse(result.containsKey(12)); 59 | } 60 | // Hint: 61 | // 62 | // Use Collectors.groupingBy(). 63 | // 64 | 65 | 66 | /** 67 | * Categorize the words from the text file into a map, where the map's key 68 | * is the length of each word, and the value corresponding to a key is a 69 | * count of words of that length. Don't bother with uniqueness or lower- 70 | * casing the words. This is the same as the previous exercise except 71 | * the map values are the count of words instead of a list of words. 72 | * 73 | * @throws IOException 74 | */ 75 | @Test @Ignore 76 | public void f2_mapLengthToWordCount() throws IOException { 77 | Map result = null; // TODO 78 | 79 | assertEquals(Map.ofEntries(entry( 1, 1L), 80 | entry( 2, 11L), 81 | entry( 3, 28L), 82 | entry( 4, 21L), 83 | entry( 5, 16L), 84 | entry( 6, 12L), 85 | entry( 7, 10L), 86 | entry( 8, 3L), 87 | entry( 9, 2L), 88 | entry(10, 2L), 89 | entry(11, 1L)), 90 | result); 91 | } 92 | // Hint 1: 93 | // 94 | // Use the overload of Collectors.groupingBy() that has 95 | // a "downstream" parameter. 96 | // 97 | // Hint 2: 98 | // 99 | // Use Collectors.counting(). 100 | // 101 | 102 | 103 | /** 104 | * Gather the words from the text file into a map, accumulating a count of 105 | * the number of occurrences of each word. Don't worry about upper case and 106 | * lower case. Extra challenge: implement two solutions, one that uses 107 | * groupingBy() and the other that uses toMap(). 108 | * 109 | * @throws IOException 110 | */ 111 | @Test @Ignore 112 | public void f3_wordFrequencies() throws IOException { 113 | Map result = null; // TODO 114 | 115 | assertEquals(2L, (long)result.get("tender")); 116 | assertEquals(6L, (long)result.get("the")); 117 | assertEquals(1L, (long)result.get("churl")); 118 | assertEquals(2L, (long)result.get("thine")); 119 | assertEquals(1L, (long)result.get("world")); 120 | assertEquals(4L, (long)result.get("thy")); 121 | assertEquals(3L, (long)result.get("self")); 122 | assertFalse(result.containsKey("lambda")); 123 | } 124 | // Hint 1: 125 | // 126 | // For Collectors.groupingBy(), consider that each word needs to be in 127 | // a category of its own, that is, each word is categorized as itself. 128 | // 129 | // Hint 2: 130 | // 131 | // For Collectors.toMap(), the first occurrence of a word should be mapped to 1. 132 | // If two elements of the Stream are generating the same key, you will need to 133 | // provide a merging function. 134 | // 135 | 136 | 137 | /** 138 | * From the words in the text file, create nested maps, where the outer map is a 139 | * map from the first letter of the word to an inner map. (Use a string of length 140 | * one as the key.) The inner map, in turn, is a mapping from the length of the 141 | * word to a list of words with that length. Don't bother with any lowercasing 142 | * or uniquifying of the words. 143 | * 144 | * For example, given the words "foo bar baz bazz foo" the string 145 | * representation of the result would be: 146 | * {b={3=[bar, baz], 4=[bazz]}, f={3=[foo, foo]}} 147 | * 148 | * @throws IOException 149 | */ 150 | @Test @Ignore 151 | public void f4_nestedMaps() throws IOException { 152 | Map>> result = null; // TODO 153 | 154 | assertEquals("[abundance]", result.get("a").get(9).toString()); 155 | assertEquals("[by, be, by]", result.get("b").get(2).toString()); 156 | assertEquals("[flame, fresh]", result.get("f").get(5).toString()); 157 | assertEquals("[gaudy, grave]", result.get("g").get(5).toString()); 158 | assertEquals("[should, spring]", result.get("s").get(6).toString()); 159 | assertEquals("[substantial]", result.get("s").get(11).toString()); 160 | assertEquals("[the, thy, thy, thy, too, the, the, thy, the, the, the]", 161 | result.get("t").get(3).toString()); 162 | assertEquals("[where, waste, world]", result.get("w").get(5).toString()); 163 | } 164 | // Hint 1: 165 | // 166 | // The nested map structure that's desired is the result of applying a 167 | // "downstream" collector that's the same operation as the first-level collector. 168 | // 169 | // Hint 2: 170 | // 171 | // Both collection operations are Collectors.groupingBy(). 172 | // 173 | 174 | 175 | /** 176 | * Given a stream of integers, compute separate sums of the even and odd values 177 | * in this stream. Since the input is a stream, this necessitates making a single 178 | * pass over the input. 179 | */ 180 | @Test @Ignore 181 | public void f5_separateOddEvenSums() { 182 | IntStream input = new Random(987523).ints(20, 0, 100); 183 | 184 | int sumEvens = 0; // TODO 185 | int sumOdds = 0; // TODO 186 | 187 | assertEquals(516, sumEvens); 188 | assertEquals(614, sumOdds); 189 | } 190 | // Hint 1: 191 | // 192 | // Use Collectors.partitioningBy(). 193 | // 194 | // Hint 2: 195 | // 196 | // The collect(Collector) method we need is defined on Stream, but not on 197 | // IntStream, LongStream or DoubleStream. 198 | // 199 | 200 | 201 | /** 202 | * Given a stream of strings, accumulate (collect) them into the result string 203 | * by inserting the input string at both the beginning and end. For example, given 204 | * input strings "x" and "y" the result should be "yxxy". Note: the input stream 205 | * is a parallel stream, so you MUST write a proper combiner function to get the 206 | * correct result. 207 | */ 208 | @Test @Ignore 209 | public void f6_insertBeginningAndEnd() { 210 | Stream input = List.of( 211 | "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", 212 | "k", "l", "m", "n", "o", "p", "q", "r", "s", "t") 213 | .parallelStream(); 214 | 215 | String result = input.collect(null, null, null).toString(); 216 | // TODO fill in lambda expressions or method references 217 | // in place of the nulls in the line above. 218 | 219 | assertEquals("tsrqponmlkjihgfedcbaabcdefghijklmnopqrst", result); 220 | } 221 | // Hint 1: 222 | // 223 | // The collector state (that is, the object being accumulated and 224 | // combined) can be a single StringBuilder, which is manipulated 225 | // by lambda expressions in the three-arg form of the collect() method. 226 | // 227 | // Hint 2: 228 | // 229 | // The combiner function must take its second argument and merge 230 | // it into the first argument, mutating the first argument. 231 | // 232 | // Hint 3: 233 | // 234 | // The second argument to the combiner function happens AFTER the first 235 | // argument in encounter order, so the second argument needs to be split 236 | // in half and prepended/appended to the first argument. 237 | // 238 | 239 | /** 240 | * Count the total number of words and the number of distinct, lower case 241 | * words in a stream, in one pass. This exercise uses a helper class 242 | * that defines methods that are called by the Stream.collect() method. 243 | * Your task is to fill in the implementation of the accumulate() and 244 | * combine() methods in the helper class. You don't need to modify the 245 | * test method itself. 246 | * 247 | * The stream is run in parallel, so you must write a combine() method 248 | * that works properly. 249 | */ 250 | static class TotalAndDistinct { 251 | private int count = 0; 252 | private final Set set = new HashSet<>(); 253 | 254 | // rely on implicit no-arg constructor 255 | 256 | void accumulate(String s) { 257 | // TODO write code to accumulate a single string into this object 258 | } 259 | 260 | void combine(TotalAndDistinct other) { 261 | // TODO write code to combine the other object into this one 262 | } 263 | 264 | int getTotalCount() { return count; } 265 | int getDistinctCount() { return set.size(); } 266 | } 267 | // Hint: 268 | // 269 | // The operations you need to write are actually quite simple. 270 | // Don't overthink it. 271 | // 272 | 273 | @Test @Ignore 274 | public void f7_countTotalAndDistinctWords() { 275 | List allWords = reader.lines() 276 | .map(String::toLowerCase) 277 | .flatMap(line -> SPLIT_PATTERN.splitAsStream(line)) 278 | .collect(Collectors.toList()); 279 | 280 | TotalAndDistinct totalAndDistinct = 281 | Collections.nCopies(100, allWords) 282 | .parallelStream() 283 | .flatMap(List::stream) 284 | .collect(TotalAndDistinct::new, 285 | TotalAndDistinct::accumulate, 286 | TotalAndDistinct::combine); 287 | 288 | assertEquals("distinct count", 81, totalAndDistinct.getDistinctCount()); 289 | assertEquals("total count", 10700, totalAndDistinct.getTotalCount()); 290 | } 291 | 292 | // ======================================================== 293 | // END OF EXERCISES 294 | // TEST INFRASTRUCTURE IS BELOW 295 | // ======================================================== 296 | 297 | 298 | // Pattern for splitting a string into words 299 | static final Pattern SPLIT_PATTERN = Pattern.compile("[- .:,]+"); 300 | 301 | private BufferedReader reader; 302 | 303 | @Before 304 | public void z_setUpBufferedReader() throws IOException { 305 | reader = Files.newBufferedReader( 306 | Paths.get("SonnetI.txt"), StandardCharsets.UTF_8); 307 | } 308 | 309 | @After 310 | public void z_closeBufferedReader() throws IOException { 311 | reader.close(); 312 | } 313 | 314 | } 315 | -------------------------------------------------------------------------------- /LambdaLab/test/exercises/G_MatcherScanner.java: -------------------------------------------------------------------------------- 1 | package exercises; 2 | 3 | import java.io.IOException; 4 | import java.nio.charset.StandardCharsets; 5 | import java.nio.file.Files; 6 | import java.nio.file.Paths; 7 | import java.util.Comparator; 8 | import java.util.regex.Pattern; 9 | import java.util.regex.Matcher; 10 | import java.util.regex.MatchResult; 11 | import java.util.Set; 12 | import java.util.Scanner; 13 | import java.util.stream.Collectors; 14 | 15 | import org.junit.After; 16 | import org.junit.Before; 17 | import org.junit.Ignore; 18 | import org.junit.Test; 19 | 20 | import static org.junit.Assert.assertEquals; 21 | import static org.junit.Assert.assertFalse; 22 | import static org.junit.Assert.assertTrue; 23 | 24 | /** 25 | * JDK 9 added several new streams-producing APIs in the java.util.regex.Matcher 26 | * and java.util.Scanner classes. These APIs make it even easier to process text 27 | * using pattern matching and fluent streams pipelines. The exercises in this file 28 | * use the new JDK 9 pattern matching APIs. 29 | */ 30 | public class G_MatcherScanner { 31 | /** 32 | * Shakespeare used "poetic contractions" which (for purposes of this exercise) 33 | * are words that have an apostrophe (') in a position farther than one letter from 34 | * the end of the word. An example of modern English contraction is "can't" where 35 | * the apostrophe precedes the last letter of the word. An example of a poetic 36 | * contraction is "o'er" (over) where the apostrophe occurs earlier in the word. 37 | * 38 | * Use the Pattern WORD_PAT (defined below) to match words with an apostrophe 39 | * earlier in the word. Match the text from Shakespeare's first sonnet, which has 40 | * been loaded into the String variable SONNET, using the Matcher class, and 41 | * process the results using a Stream. 42 | */ 43 | @Test @Ignore 44 | public void g1_wordsWithApostrophes() { 45 | Set result = null; // TODO 46 | 47 | assertEquals(Set.of("Feed'st", "mak'st"), result); 48 | } 49 | // Hint: 50 | // 51 | // Use the Matcher.results() method, then convert each MatchResult into a String. 52 | // 53 | 54 | /** 55 | * Perform the same task as in exercise g1, except using Scanner instead of Matcher. 56 | * Use the Scanner class to process the String variable SONNET, matching words as 57 | * described above using WORD_PAT. 58 | */ 59 | @Test @Ignore 60 | public void g2_wordsWithApostrophes() { 61 | Set result = null; // TODO 62 | 63 | assertEquals(Set.of("Feed'st", "mak'st"), result); 64 | } 65 | // Hint: 66 | // 67 | // Use the Scanner.findAll() method, then convert each MatchResult into a String. 68 | // 69 | 70 | /** 71 | * Find all vowel trigraphs (that is, sequences of three consecutive vowels) 72 | * in the string variable SONNET. Replace each matching substring with 73 | * the substring converted to upper case and surrounded by square brackets "[]". 74 | * Use the predefined pattern TRIGRAPH_PAT to match vowel trigraphs. 75 | */ 76 | @Test @Ignore 77 | public void g3_vowelTrigraphs() { 78 | final Pattern TRIGRAPH_PAT = Pattern.compile("[aeiou]{3}", Pattern.CASE_INSENSITIVE); 79 | String result = null; // TODO 80 | 81 | assertTrue(result.contains("b[EAU]ty's")); 82 | assertEquals(614, result.length()); 83 | } 84 | // Hint: 85 | // 86 | // Use the Matcher.replaceAll() method. 87 | // 88 | 89 | /** 90 | * Use Scanner to parse the SONNET string into whitespace-separated tokens. 91 | * Scanner's default token delimiter is whitespace, so no additional setup 92 | * needs to be done. Then, find the first such token that is of length 10 93 | * or more. 94 | * 95 | * (This task can be performed with String.split() or Pattern.splitAsStream(), 96 | * but the advantage of Scanner is that it operate on a file, an InputStream, 97 | * or a Channel, and all the input need not be loaded into memory.) 98 | */ 99 | @Test @Ignore 100 | public void g4_firstLongWhitespaceSeparatedToken() { 101 | String result = null; // TODO 102 | 103 | assertEquals("contracted", result); 104 | } 105 | // Hint: 106 | // 107 | // Use the Scanner.tokens() method. 108 | // 109 | 110 | 111 | // ======================================================== 112 | // END OF EXERCISES 113 | // TEST INFRASTRUCTURE IS BELOW 114 | // ======================================================== 115 | 116 | /** 117 | * Pattern for use in exercises g1 and g2. This regex matches a word that contains 118 | * an apostrophe, by matching a word boundary, one or more letters, an apostrophe, 119 | * two or more letters, and a word boundary. Matching is case insensitive. 120 | */ 121 | static final Pattern WORD_PAT = Pattern.compile("\\b[a-z]+'[a-z]{2,}\\b", Pattern.CASE_INSENSITIVE); 122 | 123 | /** 124 | * The text of Shakespeare's first sonnet, in a string. Note, this string 125 | * contains newline characters. 126 | */ 127 | private String SONNET; 128 | 129 | @Before 130 | public void z_readFileIntoString() throws IOException { 131 | SONNET = new String(Files.readAllBytes(Paths.get("SonnetI.txt")), 132 | StandardCharsets.UTF_8); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /LambdaLab/test/exercises/H_Challenges.java: -------------------------------------------------------------------------------- 1 | package exercises; 2 | 3 | import java.io.Serializable; 4 | import java.lang.reflect.Modifier; 5 | import java.util.AbstractMap; 6 | import java.util.ArrayDeque; 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | import java.util.Collection; 10 | import java.util.HashMap; 11 | import java.util.HashSet; 12 | import java.util.LinkedHashSet; 13 | import java.util.List; 14 | import java.util.Map; 15 | import java.util.Objects; 16 | import java.util.OptionalInt; 17 | import java.util.RandomAccess; 18 | import java.util.Set; 19 | import java.util.function.Function; 20 | import java.util.function.IntFunction; 21 | import java.util.function.Predicate; 22 | import java.util.function.Supplier; 23 | import java.util.stream.Collector; 24 | import java.util.stream.Collectors; 25 | import java.util.stream.IntStream; 26 | import java.util.stream.Stream; 27 | 28 | import org.junit.Ignore; 29 | import org.junit.Test; 30 | 31 | import static org.junit.Assert.assertEquals; 32 | import static org.junit.Assert.assertFalse; 33 | import static org.junit.Assert.assertTrue; 34 | 35 | public class H_Challenges { 36 | 37 | /** 38 | * Denormalize this map. The input is a map whose keys are the number of legs of an animal 39 | * and whose values are lists of names of animals. Run through the map and generate a 40 | * "denormalized" list of strings describing the animal, with the animal's name separated 41 | * by a colon from the number of legs it has. The ordering in the output list is not 42 | * considered significant. 43 | * 44 | * Input is Map>: 45 | * { 4=["ibex", "hedgehog", "wombat"], 46 | * 6=["ant", "beetle", "cricket"], 47 | * ... 48 | * } 49 | * 50 | * Output should be a List: 51 | * [ "ibex:4", 52 | * "hedgehog:4", 53 | * "wombat:4", 54 | * "ant:6", 55 | * "beetle:6", 56 | * "cricket:6", 57 | * ... 58 | * ] 59 | */ 60 | @Test @Ignore 61 | public void h1_denormalizeMap() { 62 | Map> input = new HashMap<>(); 63 | input.put(4, Arrays.asList("ibex", "hedgehog", "wombat")); 64 | input.put(6, Arrays.asList("ant", "beetle", "cricket")); 65 | input.put(8, Arrays.asList("octopus", "spider", "squid")); 66 | input.put(10, Arrays.asList("crab", "lobster", "scorpion")); 67 | input.put(750, Arrays.asList("millipede")); 68 | 69 | List result = null; // TODO 70 | 71 | assertEquals(13, result.size()); 72 | assertTrue(result.contains("ibex:4")); 73 | assertTrue(result.contains("hedgehog:4")); 74 | assertTrue(result.contains("wombat:4")); 75 | assertTrue(result.contains("ant:6")); 76 | assertTrue(result.contains("beetle:6")); 77 | assertTrue(result.contains("cricket:6")); 78 | assertTrue(result.contains("octopus:8")); 79 | assertTrue(result.contains("spider:8")); 80 | assertTrue(result.contains("squid:8")); 81 | assertTrue(result.contains("crab:10")); 82 | assertTrue(result.contains("lobster:10")); 83 | assertTrue(result.contains("scorpion:10")); 84 | assertTrue(result.contains("millipede:750")); 85 | } 86 | // Hint 1: 87 | // 88 | // There are several ways to approach this. You could use a stream of map keys, 89 | // a stream of map entries, or nested forEach() methods. 90 | // 91 | // Hint 2: 92 | // 93 | // If you use streams, consider using Stream.flatMap(). 94 | // 95 | 96 | 97 | /** 98 | * Invert a "multi-map". (From an idea by Paul Sandoz) 99 | * 100 | * Given a Map>, convert it to Map>. 101 | * Each set member of the input map's values becomes a key in 102 | * the result map. Each key in the input map becomes a set member 103 | * of the values of the result map. In the input map, an item 104 | * may appear in the value set of multiple keys. In the result 105 | * map, that item will be a key, and its value set will be 106 | * its corresopnding keys from the input map. 107 | * 108 | * In this case the input is Map> 109 | * and the result is Map>. 110 | * 111 | * For example, if the input map is 112 | * {p=[10, 20], q=[20, 30]} 113 | * then the result map should be 114 | * {10=[p], 20=[p, q], 30=[q]} 115 | * irrespective of ordering. Note that the Integer 20 appears 116 | * in the value sets for both p and q in the input map. Therefore, 117 | * in the result map, there should be a mapping with 20 as the key 118 | * and p and q as its value set. 119 | * 120 | * It is possible to accomplish this task using a single stream 121 | * pipeline (not counting nested streams), that is, in a single pass 122 | * over the input, without storing anything in a temporary collection. 123 | */ 124 | @Test @Ignore 125 | public void h2_invertMultiMap() { 126 | Map> input = new HashMap<>(); 127 | input.put("a", new HashSet<>(Arrays.asList(1, 2))); 128 | input.put("b", new HashSet<>(Arrays.asList(2, 3))); 129 | input.put("c", new HashSet<>(Arrays.asList(1, 3))); 130 | input.put("d", new HashSet<>(Arrays.asList(1, 4))); 131 | input.put("e", new HashSet<>(Arrays.asList(2, 4))); 132 | input.put("f", new HashSet<>(Arrays.asList(3, 4))); 133 | 134 | Map> result = null; // TODO 135 | 136 | assertEquals(new HashSet<>(Arrays.asList("a", "c", "d")), result.get(1)); 137 | assertEquals(new HashSet<>(Arrays.asList("a", "b", "e")), result.get(2)); 138 | assertEquals(new HashSet<>(Arrays.asList("b", "c", "f")), result.get(3)); 139 | assertEquals(new HashSet<>(Arrays.asList("d", "e", "f")), result.get(4)); 140 | assertEquals(4, result.size()); 141 | } 142 | // Hint 1: 143 | // 144 | // A general approach is to flatten the input structure in one stage 145 | // of the pipeline and then to create the result structure using a collector. 146 | // 147 | // Hint 2: 148 | // 149 | // A useful intermediate data structure after the flattening step 150 | // is a pair of items. You can write your own pair class, or you can 151 | // use a pre-existing class like AbstractMap.SimpleEntry. 152 | // 153 | 154 | 155 | /** 156 | * Select the longest words from an input stream. That is, select the words 157 | * whose lengths are equal to the maximum word length. For this exercise, 158 | * you must compute the result in a single pass over the input stream. 159 | * The type of the input is a Stream, so you cannot access elements at random. 160 | * The stream is run in parallel, so the combiner function must be correct. 161 | */ 162 | @Test @Ignore 163 | public void h3_selectLongestWordsOnePass() { 164 | Stream input = Stream.of( 165 | "alfa", "bravo", "charlie", "delta", 166 | "echo", "foxtrot", "golf", "hotel").parallel(); 167 | 168 | List result = input.collect( 169 | Collector.of(null, null, null, null)); 170 | // TODO implement a collector by replacing the nulls above 171 | 172 | assertEquals(Arrays.asList("charlie", "foxtrot"), result); 173 | } 174 | // Hint: 175 | // 176 | // There are several ways to solve this exercise, but one approach is to 177 | // create a helper class with four functions, and then pass method 178 | // references to these functions to the Collector.of() method. 179 | // 180 | 181 | 182 | 183 | /** 184 | * Given a string, split it into a list of strings consisting of 185 | * consecutive characters from the original string. Note: this is 186 | * similar to Python's itertools.groupby function, but it differs 187 | * from Java's Collectors.groupingBy() collector. 188 | */ 189 | @Test @Ignore 190 | public void h4_splitCharacterRuns() { 191 | String input = "aaaaabbccccdeeeeeeaaafff"; 192 | 193 | List result = null; // TODO 194 | 195 | assertEquals("[aaaaa, bb, cccc, d, eeeeee, aaa, fff]", result.toString()); 196 | } 197 | // Hint: 198 | // 199 | // One possibility is a two-pass approach: one pass to gather data about 200 | // the boundaries between the runs, and the second to create the substrings 201 | // based on output from the first. 202 | // 203 | 204 | /** 205 | * Given a parallel stream of strings, collect them into a collection in reverse order. 206 | * Since the stream is parallel, you MUST write a proper combiner function in order to get 207 | * the correct result. 208 | */ 209 | @Test @Ignore 210 | public void h5_reversingCollector() { 211 | Stream input = 212 | IntStream.range(0, 100).mapToObj(String::valueOf).parallel(); 213 | 214 | Collection result = 215 | input.collect(Collector.of(null, null, null)); 216 | // TODO fill in collector functions above 217 | 218 | assertEquals( 219 | IntStream.range(0, 100) 220 | .map(i -> 99 - i) 221 | .mapToObj(String::valueOf) 222 | .collect(Collectors.toList()), 223 | new ArrayList<>(result)); 224 | } 225 | // Hint 1: 226 | // 227 | // ArrayDeque supports fast insertion at the front. 228 | // 229 | // Hint 2: 230 | // 231 | // Be careful with ordering of the arguments and results in the combiner. 232 | // 233 | 234 | /** 235 | * Given an array of int, find the int value that occurs a majority 236 | * of times in the array (that is, strictly more than half of the 237 | * elements are that value), and return that int value in an OptionalInt. 238 | * Note, return the majority int value, not the number of times it occurs. 239 | * If there is no majority value, return an empty OptionalInt. 240 | * 241 | * For example, given an input array [11, 12, 12] the result should be 242 | * an OptionalInt containing 12. Given an input array [11, 12, 13] 243 | * the result should be an empty OptionalInt. 244 | */ 245 | 246 | OptionalInt majority(int[] array) { 247 | return null; // TODO 248 | } 249 | // Hint: 250 | // 251 | // A two-pass approach may be called for here: a counting pass 252 | // and a majority-finding pass. 253 | // 254 | 255 | @Test @Ignore 256 | public void h6_majority() { 257 | int[] array1 = { 13, 13, 24, 35, 24, 24, 35, 24, 24 }; 258 | int[] array2 = { 13, 13, 24, 35, 24, 24, 35, 24 }; 259 | 260 | OptionalInt result1 = majority(array1); 261 | OptionalInt result2 = majority(array2); 262 | 263 | assertEquals(OptionalInt.of(24), result1); 264 | assertFalse(result2.isPresent()); 265 | } 266 | 267 | /** 268 | * Write a method that takes an IntFunction and returns a Supplier. 269 | * An IntFunction takes an int as an argument and returns some object. 270 | * A Supplier takes no arguments and returns some object. The object 271 | * type in this case is a Shoe that has a single attribute, its size. 272 | * The goal is to write a lambda expression that uses the IntFunction 273 | * and size values provided as arguments, and that returns a Supplier 274 | * that embodies both of them. This is an example of a functional 275 | * programming concept called "partial application." 276 | */ 277 | Supplier makeShoeSupplier(IntFunction ifunc, int size) { 278 | return null; // TODO 279 | } 280 | // Hint: 281 | // 282 | // You don't want to return the result of calling the IntFunction. 283 | // Instead, you want to return a lambda that calls the IntFunction. 284 | // 285 | 286 | static class Shoe { 287 | final int size; 288 | public Shoe(int size) { this.size = size; } 289 | public int hashCode() { return size ^ 0xcafebabe; } 290 | public boolean equals(Object other) { 291 | return (other instanceof Shoe) && this.size == ((Shoe)other).size; 292 | } 293 | } 294 | 295 | @Test @Ignore 296 | public void h7_shoemaker() { 297 | Supplier sup1 = makeShoeSupplier(Shoe::new, 9); 298 | Supplier sup2 = makeShoeSupplier(Shoe::new, 13); 299 | 300 | Shoe shoe1 = sup1.get(); 301 | Shoe shoe2 = sup1.get(); 302 | Shoe shoe3 = sup2.get(); 303 | Shoe shoe4 = sup2.get(); 304 | 305 | assertTrue(shoe1 != shoe2); 306 | assertTrue(shoe3 != shoe4); 307 | assertEquals(new Shoe(9), shoe1); 308 | assertEquals(shoe1, shoe2); 309 | assertEquals(new Shoe(13), shoe3); 310 | assertEquals(shoe3, shoe4); 311 | } 312 | 313 | /** 314 | * Write a method that extracts all the superclasses of ArrayList and 315 | * their implemented classes. Filter out the abstract classes, then 316 | * create a map with two boolean keys, true is associated to the interfaces 317 | * and false with the concrete classes. 318 | */ 319 | @Test @Ignore 320 | public void h8_mapOfClassesAndInterfaces() { 321 | 322 | Class origin = ArrayList.class; 323 | Map>> result = null; // TODO 324 | 325 | assertEquals(Map.of(false, Set.of(ArrayList.class, Object.class), 326 | true, Set.of(List.class, RandomAccess.class, Cloneable.class, 327 | Serializable.class, Collection.class)), 328 | result); 329 | } 330 | // Hint: 331 | // 332 | // The beginning of this challenge begins with the same kind of pattern 333 | // as the E8 intermediate exercise. 334 | // The interfaces are returned in an array, so one can put them in a stream 335 | // using Arrays.stream(). To add the class to that stream, you can also 336 | // use Stream.of() and flatMap the result to have the final stream. 337 | // Writing the filter step is just a matter of creating the right predicate. 338 | // Then the partioningBy collector will build the map. 339 | // 340 | 341 | /** 342 | * Write a method that extracts all the superclasses and 343 | * their implemented classes. Filter out the abstract classes, then 344 | * create a map with two boolean keys, true is associated to the interfaces 345 | * and false with the concrete classes. Do that for the provided classes, and 346 | * arrange the result in a Map with those classes as the keys. 347 | */ 348 | @Test @Ignore 349 | public void h9_mapOfMapsOfClassesAndInterfaces() { 350 | 351 | List> origin = List.of(ArrayList.class, HashSet.class, LinkedHashSet.class); 352 | Map, Map>>> result = null; // TODO 353 | 354 | assertEquals( 355 | Map.of( 356 | ArrayList.class, 357 | Map.of(false, Set.of(ArrayList.class, Object.class), 358 | true, Set.of(List.class, RandomAccess.class, Cloneable.class, 359 | Serializable.class, Collection.class)), 360 | HashSet.class, 361 | Map.of(false, Set.of(HashSet.class, Object.class), 362 | true, Set.of(Set.class, Cloneable.class, 363 | Serializable.class, Collection.class)), 364 | LinkedHashSet.class, 365 | Map.of(false, Set.of(LinkedHashSet.class, HashSet.class, Object.class), 366 | true, Set.of(Set.class, Cloneable.class, 367 | Serializable.class, Collection.class))), 368 | result); 369 | } 370 | // Hint: 371 | // 372 | // The trick here is to write the whole processing of the previous 373 | // G8 challenge as a single collector. Once this is done, just pass 374 | // this collector as the downstream collector of a groupingBy. 375 | // A filtering collector and a flatMapping collector have been added 376 | // to JDK9. 377 | // 378 | } 379 | -------------------------------------------------------------------------------- /LambdaLab/test/model/Person.java: -------------------------------------------------------------------------------- 1 | package model; 2 | 3 | public class Person { 4 | 5 | private String lastName; 6 | private String firstName; 7 | private int age; 8 | 9 | public Person(String firstName, String lastName, int age) { 10 | this.lastName = lastName; 11 | this.firstName = firstName; 12 | this.age = age; 13 | } 14 | 15 | public String getLastName() { 16 | return lastName; 17 | } 18 | 19 | public void setLastName(String lastName) { 20 | this.lastName = lastName; 21 | } 22 | 23 | public String getFirstName() { 24 | return firstName; 25 | } 26 | 27 | public void setFirstName(String firstName) { 28 | this.firstName = firstName; 29 | } 30 | 31 | public int getAge() { 32 | return age; 33 | } 34 | 35 | public void setAge(int age) { 36 | this.age = age; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /LambdaLab/test/solutions/A_Lambdas.java: -------------------------------------------------------------------------------- 1 | package solutions; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | import java.util.function.BiFunction; 7 | 8 | import java.util.function.Consumer; 9 | import java.util.function.Function; 10 | import java.util.function.Predicate; 11 | import java.util.function.Supplier; 12 | 13 | import org.junit.Ignore; 14 | import org.junit.Test; 15 | 16 | import static org.junit.Assert.assertEquals; 17 | import static org.junit.Assert.assertFalse; 18 | import static org.junit.Assert.assertTrue; 19 | 20 | /** 21 | * This set of exercises is about lambdas and method references. 22 | * You will write a lambda or method reference corresponding to 23 | * each of several different functional interfaces. Each exercise 24 | * is named after the functional interface intended to be used 25 | * as the solution. 26 | */ 27 | public class A_Lambdas { 28 | 29 | /** 30 | * Write a lambda expression that is a predicate 31 | * that tests whether a string is longer than four characters. 32 | */ 33 | @Test 34 | public void a_predicate1() { 35 | //TODO//Predicate pred = null; 36 | //BEGINREMOVE 37 | Predicate pred = s -> s.length() > 4; 38 | //ENDREMOVE 39 | 40 | assertTrue(pred.test("abcde")); 41 | assertFalse(pred.test("abcd")); 42 | } 43 | 44 | /** 45 | * Write a lambda expression that is a predicate 46 | * that tests whether a string is empty. 47 | */ 48 | @Test 49 | public void a_predicate2() { 50 | //TODO//Predicate pred = null; 51 | //BEGINREMOVE 52 | Predicate pred = s -> s.isEmpty(); 53 | //ENDREMOVE 54 | 55 | assertTrue(pred.test("")); 56 | assertFalse(pred.test("a")); 57 | } 58 | 59 | /** 60 | * Write an unbound method reference that is a predicate 61 | * that tests whether a string is empty. An unbound method 62 | * reference has a class name on the left-hand side of the :: 63 | * operator: 64 | * 65 | * classname::methodname 66 | */ 67 | @Test 68 | public void a_predicate3() { 69 | //TODO//Predicate pred = null; 70 | //BEGINREMOVE 71 | Predicate pred = String::isEmpty; 72 | //ENDREMOVE 73 | 74 | assertTrue(pred.test("")); 75 | assertFalse(pred.test("a")); 76 | } 77 | // Hint: 78 | // 79 | // Copy the lambda expression from the previous exercise and then pop 80 | // up the menu over the "light bulb" icon in the left margin. This menu 81 | // has an option to convert the lambda to a method reference. (The exact 82 | // gesture will vary among IDEs.) 83 | // 84 | 85 | /** 86 | * Create a predicate that returns true if both predicates 87 | * startsWithJ and lengthIs7 hold. 88 | */ 89 | @Test 90 | public void a_predicate4() { 91 | Predicate startsWithJ = s -> s.startsWith("J"); 92 | Predicate lengthIs7 = s -> s.length() == 7; 93 | 94 | //TODO//Predicate startsWithJAndLengthIs7 = null; 95 | //BEGINREMOVE 96 | Predicate startsWithJAndLengthIs7 = startsWithJ.and(lengthIs7); 97 | //ENDREMOVE 98 | 99 | assertFalse(startsWithJAndLengthIs7.test("Hello")); 100 | assertFalse(startsWithJAndLengthIs7.test("HelloJ1")); 101 | assertFalse(startsWithJAndLengthIs7.test("Java1")); 102 | assertTrue(startsWithJAndLengthIs7.test("JavaOne")); 103 | } 104 | // Hint: 105 | // 106 | // see java.util.function.Predicate.and() 107 | // 108 | 109 | /** 110 | * Create a predicate that is true if the length of the provided string 111 | * is 9 or the provided string equals ERROR. 112 | */ 113 | @Test 114 | public void a_predicate5() { 115 | Predicate lengthIs9 = s -> s.length() == 9; 116 | Predicate equalsError = "ERROR"::equals; 117 | // Note: this could also be: Predicate.isEqual("ERROR") 118 | 119 | //TODO//Predicate lengthIs9orError = null; 120 | //BEGINREMOVE 121 | Predicate lengthIs9orError = lengthIs9.or(equalsError); 122 | //ENDREMOVE 123 | 124 | assertFalse(lengthIs9orError.test("Hello")); 125 | assertTrue(lengthIs9orError.test("Hello J1!")); 126 | assertTrue(lengthIs9orError.test("ERROR")); 127 | assertFalse(lengthIs9orError.test("Error")); 128 | } 129 | // Hint: 130 | // 131 | // see java.util.function.Predicate.or() 132 | // 133 | 134 | /** 135 | * Write a lambda expression that wraps the given 136 | * string in parentheses. 137 | */ 138 | @Test 139 | public void b_function1() { 140 | //TODO//Function func = null; 141 | //BEGINREMOVE 142 | Function func = s -> "(" + s + ")"; 143 | //ENDREMOVE 144 | 145 | assertEquals("(abc)", func.apply("abc")); 146 | } 147 | 148 | /** 149 | * Write a lambda expression that converts the 150 | * given string to upper case. 151 | */ 152 | @Test 153 | public void b_function2() { 154 | //TODO//Function func = null; 155 | //BEGINREMOVE 156 | Function func = s -> s.toUpperCase(); 157 | //ENDREMOVE 158 | 159 | assertEquals("ABC", func.apply("abc")); 160 | } 161 | 162 | /** 163 | * Write an unbound method reference that converts the 164 | * given string to upper case. 165 | */ 166 | @Test 167 | public void b_function3() { 168 | //TODO//Function func = null; 169 | //BEGINREMOVE 170 | Function func = String::toUpperCase; 171 | //ENDREMOVE 172 | 173 | assertEquals("ABC", func.apply("abc")); 174 | } 175 | 176 | /** 177 | * Given two Functions, one that converts a null reference to an 178 | * empty string, and another that gets the length of a string, 179 | * create a single function converts nulls and then gets the 180 | * string's length. 181 | */ 182 | @Test 183 | public void b_function4() { 184 | Function unNullify = s -> s == null ? "" : s; 185 | Function length = String::length; 186 | 187 | //TODO//Function lengthBis = null; 188 | //BEGINREMOVE 189 | Function lengthBis = unNullify.andThen(length); 190 | // Alternatively: 191 | // Function lengthBis = length.compose(unNullify); 192 | //ENDREMOVE 193 | 194 | assertEquals((Integer)14, lengthBis.apply("Hello JavaOne!")); 195 | assertEquals((Integer)0, lengthBis.apply("")); 196 | assertEquals((Integer)0, lengthBis.apply(null)); 197 | } 198 | // Hint: 199 | // 200 | // See java.util.Function.andThen() or java.util.Function.compose() 201 | // 202 | 203 | /** 204 | * Write a lambda expression that appends the 205 | * string "abc" to the given StringBuilder. 206 | */ 207 | @Test 208 | public void c_consumer1() { 209 | //TODO//Consumer cons = null; 210 | //BEGINREMOVE 211 | Consumer cons = sb -> sb.append("abc"); 212 | //ENDREMOVE 213 | 214 | StringBuilder sb = new StringBuilder("xyz"); 215 | cons.accept(sb); 216 | assertEquals("xyzabc", sb.toString()); 217 | } 218 | 219 | /** 220 | * Write a lambda expression that clears the given list. 221 | */ 222 | @Test 223 | public void c_consumer2() { 224 | //TODO//Consumer> cons = null; 225 | //BEGINREMOVE 226 | Consumer> cons = list -> list.clear(); 227 | //ENDREMOVE 228 | 229 | List list = new ArrayList<>(List.of("a", "b", "c")); 230 | cons.accept(list); 231 | assertTrue(list.isEmpty()); 232 | } 233 | 234 | /** 235 | * Write an unbound method reference that clears the given list. 236 | */ 237 | @Test 238 | public void c_consumer3() { 239 | //TODO//Consumer> cons = null; 240 | //BEGINREMOVE 241 | Consumer> cons = List::clear; 242 | //ENDREMOVE 243 | 244 | List list = new ArrayList<>(List.of("a", "b", "c")); 245 | cons.accept(list); 246 | assertTrue(list.isEmpty()); 247 | } 248 | 249 | /** 250 | * Given two consumers, create a consumer that passes the String to the 251 | * first consumer, then to the second. 252 | */ 253 | @Test 254 | public void c_consumer4() { 255 | Consumer> c1 = list -> list.add("first"); 256 | Consumer> c2 = list -> list.add("second"); 257 | 258 | //TODO//Consumer> consumer = null; 259 | //BEGINREMOVE 260 | Consumer> consumer = c1.andThen(c2); 261 | //ENDREMOVE 262 | 263 | List list = new ArrayList<>(List.of("a", "b", "c")); 264 | consumer.accept(list); 265 | assertEquals(List.of("a", "b", "c", "first", "second"), list); 266 | } 267 | // Hint: 268 | // 269 | // see java.util.function.Consumer.andThen() 270 | // 271 | 272 | /** 273 | * Write a lambda expression that returns a new StringBuilder 274 | * containing the string "abc". 275 | */ 276 | @Test 277 | public void d_supplier1() { 278 | //TODO//Supplier sup = null; 279 | //BEGINREMOVE 280 | Supplier sup = () -> new StringBuilder("abc"); 281 | //ENDREMOVE 282 | 283 | assertEquals("abc", sup.get().toString()); 284 | } 285 | 286 | /** 287 | * Write a lambda expression that returns a new, empty StringBuilder. 288 | */ 289 | @Test 290 | public void d_supplier2() { 291 | //TODO//Supplier sup = null; 292 | //BEGINREMOVE 293 | Supplier sup = () -> new StringBuilder(); 294 | //ENDREMOVE 295 | 296 | assertEquals("", sup.get().toString()); 297 | } 298 | 299 | /** 300 | * Write a constructor reference that returns a new, empty StringBuilder. 301 | */ 302 | @Test 303 | public void d_supplier3() { 304 | //TODO//Supplier sup = null; 305 | //BEGINREMOVE 306 | Supplier sup = StringBuilder::new; 307 | //ENDREMOVE 308 | 309 | assertEquals("", sup.get().toString()); 310 | } 311 | 312 | /** 313 | * Write a lambda expression that, given two strings, returns the result 314 | * of concatenating the first with the second, followed by the 315 | * first again. 316 | */ 317 | @Test 318 | public void e_bifunction1() { 319 | //TODO//BiFunction bifunc = null; 320 | //BEGINREMOVE 321 | BiFunction bifunc = (s1, s2) -> s1 + s2 + s1; 322 | //ENDREMOVE 323 | 324 | assertEquals("FirstSecondFirst", bifunc.apply("First", "Second")); 325 | } 326 | 327 | /** 328 | * Write a lambda expression that returns the index of 329 | * the first occurrence of the second string within the first string, 330 | * or -1 if the second string doesn't occur within the first string. 331 | */ 332 | @Test 333 | public void e_bifunction2() { 334 | //TODO//BiFunction bifunc = null; 335 | //BEGINREMOVE 336 | BiFunction bifunc = (s1, s2) -> s1.indexOf(s2); 337 | //ENDREMOVE 338 | 339 | assertEquals(3, bifunc.apply("abcdefghi", "def").intValue()); 340 | assertEquals(-1, bifunc.apply("abcdefghi", "xyz").intValue()); 341 | } 342 | // Hint: 343 | // 344 | // The String method 345 | // public int indexOf(String) 346 | // works as a BiFunction, because the receiver (a String instance) 347 | // counts as the first argument. The argument to indexOf() becomes 348 | // the second argument to the BiFunction. 349 | // 350 | 351 | /** 352 | * Write an unbound method reference that returns the index of 353 | * the first occurrence of the second string within the first string, 354 | * or -1 if the second string doesn't occur within the first string. 355 | */ 356 | @Test 357 | public void e_bifunction3() { 358 | //TODO//BiFunction bifunc = null; 359 | //BEGINREMOVE 360 | BiFunction bifunc = String::indexOf; 361 | //ENDREMOVE 362 | 363 | assertEquals(3, bifunc.apply("abcdefghij", "def").intValue()); 364 | assertEquals(-1, bifunc.apply("abcdefghij", "xyz").intValue()); 365 | } 366 | // Hint 1: 367 | // 368 | // Try using the IDE command to convert the lambda from the previous 369 | // exercise into a method reference. 370 | // 371 | // Hint 2: 372 | // 373 | // This is just like the example above with the argument shifting. 374 | // The only difference is that arguments aren't specified in a 375 | // method reference, so overload resolution has to do more work 376 | // to find the overloaded method that matches. 377 | // 378 | 379 | 380 | /** 381 | * Write a lambda expression that appends the 'suffix' 382 | * variable (a String) to the sb variable (a StringBuilder). 383 | */ 384 | @Test 385 | public void f_runnable1() { 386 | StringBuilder sb = new StringBuilder("abc"); 387 | String suffix = "xyz"; 388 | 389 | //TODO//Runnable r = null; 390 | //BEGINREMOVE 391 | Runnable r = () -> sb.append(suffix); 392 | //ENDREMOVE 393 | 394 | r.run(); 395 | r.run(); 396 | r.run(); 397 | assertEquals("abcxyzxyzxyz", sb.toString()); 398 | } 399 | 400 | /** 401 | * Write a lambda expression that takes a string argument 402 | * and returns the index of that argument into the string 403 | * "abcdefghij", or that returns -1 if the string argument 404 | * doesn't occur. 405 | */ 406 | @Test 407 | public void g_boundMethodRef1() { 408 | //TODO//Function func = null; 409 | //BEGINREMOVE 410 | Function func = s -> "abcdefghij".indexOf(s); 411 | //ENDREMOVE 412 | 413 | assertEquals(2, func.apply("cde").intValue()); 414 | assertEquals(4, func.apply("efg").intValue()); 415 | assertEquals(-1, func.apply("xyz").intValue()); 416 | } 417 | // Hint: 418 | // 419 | // Call the indexOf() method on a string literal. 420 | // 421 | 422 | /** 423 | * Write a bound method reference that takes a string argument 424 | * and returns the index of that argument into the string 425 | * "abcdefghij", or that returns -1 if the string argument 426 | * doesn't occur. A bound method reference has an instance, 427 | * or an expression that evaluates to an instance, on the left-hand 428 | * side of the :: operator: 429 | * 430 | * myObject::methodname 431 | * 432 | * This is in contrast to an unbound method reference, which has 433 | * a classname on the left-hand side of the :: operator. 434 | */ 435 | @Test 436 | public void g_boundMethodRef2() { 437 | //TODO//Function func = null; 438 | //BEGINREMOVE 439 | Function func = "abcdefghij"::indexOf; 440 | //ENDREMOVE 441 | 442 | assertEquals(2, func.apply("cde").intValue()); 443 | assertEquals(4, func.apply("efg").intValue()); 444 | assertEquals(-1, func.apply("xyz").intValue()); 445 | } 446 | // Hint: 447 | // 448 | // Place a string literal on the left-hand side of the :: operator. 449 | // 450 | } 451 | -------------------------------------------------------------------------------- /LambdaLab/test/solutions/B_Comparators.java: -------------------------------------------------------------------------------- 1 | package solutions; 2 | 3 | import java.util.Comparator; 4 | import java.util.function.IntBinaryOperator; 5 | 6 | import org.junit.Ignore; 7 | import org.junit.Test; 8 | 9 | import static org.junit.Assert.assertEquals; 10 | import static org.junit.Assert.assertFalse; 11 | import static org.junit.Assert.assertTrue; 12 | 13 | import model.Person; 14 | 15 | /** 16 | * Exercises to create comparators using lambda expressions 17 | * and using the Comparator combinators. Some of the exercises 18 | * use a Person class, which is a simple POJO containing a last 19 | * name, first name, and age, with the obvious constructors and 20 | * getters. 21 | */ 22 | public class B_Comparators { 23 | 24 | final Person michael = new Person("Michael", "Jackson", 51); 25 | final Person rod = new Person("Rod", "Stewart", 71); 26 | final Person paul = new Person("Paul", "McCartney", 74); 27 | final Person mick = new Person("Mick", "Jagger", 73); 28 | final Person jermaine = new Person("Jermaine", "Jackson", 61); 29 | 30 | /** 31 | * Write a Comparator that compare instances of String using their length. 32 | * For instance FOUR (4 letters) is greater than TWO (three letters) 33 | */ 34 | @Test 35 | public void comparator01() { 36 | //TODO//Comparator compareByLength = null; 37 | //BEGINREMOVE 38 | Comparator compareByLength = Comparator.comparing(String::length); 39 | //ENDREMOVE 40 | 41 | assertTrue(compareByLength.compare("FOUR", "TWO") > 0); 42 | assertTrue(compareByLength.compare("ONE", "SEVEN") < 0); 43 | assertTrue(compareByLength.compare("ONE", "TWO") == 0); 44 | } 45 | // Hint: 46 | // 47 | // Check the static factory methods of the Comparator interface. Remember 48 | // how you implemented functions in the previous exercises. Write it using 49 | // a method reference. 50 | // 51 | 52 | /** 53 | * Write a Comparator that compare instances of String using their length. 54 | * If the lengths are the same, then use the alphabetical order. 55 | */ 56 | @Test 57 | public void comparator02() { 58 | //TODO//Comparator compareByLengthThenAlphabetical = null; 59 | //BEGINREMOVE 60 | Comparator compareByLengthThenAlphabetical = 61 | Comparator.comparing(String::length).thenComparing(Comparator.naturalOrder()); 62 | //ENDREMOVE 63 | 64 | assertTrue(compareByLengthThenAlphabetical.compare("FOUR", "TWO") > 0); 65 | assertTrue(compareByLengthThenAlphabetical.compare("ONE", "SEVEN") < 0); 66 | assertTrue(compareByLengthThenAlphabetical.compare("ONE", "TWO") < 0); 67 | assertTrue(compareByLengthThenAlphabetical.compare("FOUR", "FIVE") > 0); 68 | assertTrue(compareByLengthThenAlphabetical.compare("EIGHT", "EIGHT") == 0); 69 | } 70 | // Hint: 71 | // 72 | // Use the previous comparator and check the default methods of the 73 | // Comparator interface. 74 | // Check also the factory methods of the Comparator interface, and remember 75 | // that String is comparable. 76 | // 77 | 78 | /** 79 | * Write a Comparator that compares instances of Person using their lastName. 80 | */ 81 | @Test 82 | public void comparator03() { 83 | //TODO//Comparator comparebyLastName = null; 84 | //BEGINREMOVE 85 | Comparator comparebyLastName = Comparator.comparing(Person::getLastName); 86 | //ENDREMOVE 87 | 88 | assertTrue(comparebyLastName.compare(michael, rod) < 0); 89 | assertTrue(comparebyLastName.compare(paul, paul) == 0); 90 | assertTrue(comparebyLastName.compare(michael, jermaine) == 0); 91 | } 92 | // Hint: 93 | // 94 | // Check the static factory methods of the Comparator interface. Remember 95 | // how you implemented functions in the previous exercises. Write it using 96 | // a method reference. 97 | // 98 | 99 | /** 100 | * Write a Comparator that compares instances of Person using their 101 | * lastName, and if their last name is the same, uses their first name. 102 | */ 103 | @Test 104 | public void comparator04() { 105 | //TODO//Comparator comparebyLastNameThenFirstName = null; 106 | //BEGINREMOVE 107 | Comparator comparebyLastNameThenFirstName = 108 | Comparator.comparing(Person::getLastName) 109 | .thenComparing(Person::getFirstName); 110 | //ENDREMOVE 111 | 112 | assertTrue(comparebyLastNameThenFirstName.compare(michael, rod) < 0); 113 | assertTrue(comparebyLastNameThenFirstName.compare(paul, paul) == 0); 114 | assertTrue(comparebyLastNameThenFirstName.compare(michael, jermaine) > 0); 115 | } 116 | // Hint: 117 | // 118 | // Use the previous comparator and check the default methods of the Comparator interface. 119 | // 120 | 121 | /** 122 | * Write a Comparator that compares the people in the order reversed from 123 | * the one you wrote in the comparator04() exercise. That is, the person 124 | * with the greater last name should be ordered first. If two persons have 125 | * the same last name, the one with the greater first name should be 126 | * ordered first. 127 | */ 128 | @Test 129 | public void comparator05() { 130 | //TODO//Comparator comparebyLastNameThenFirstNameReversed = null; 131 | //BEGINREMOVE 132 | Comparator comparebyLastNameThenFirstNameReversed = 133 | Comparator.comparing(Person::getLastName) 134 | .thenComparing(Person::getFirstName) 135 | .reversed(); 136 | //ENDREMOVE 137 | 138 | assertFalse(comparebyLastNameThenFirstNameReversed.compare(michael, rod) < 0); 139 | assertTrue(comparebyLastNameThenFirstNameReversed.compare(paul, paul) == 0); 140 | assertFalse(comparebyLastNameThenFirstNameReversed.compare(michael, jermaine) > 0); 141 | } 142 | // Hint: 143 | // 144 | // Use the previous comparator and check the default methods of the Comparator interface. 145 | // 146 | 147 | /** 148 | * Write a Comparator that compares the people in the same order than the 149 | * one you wrote in comparator04(), but that supports null values. The null 150 | * values should be considered greater than any non-null values. 151 | */ 152 | @Test 153 | public void comparator06() { 154 | //TODO//Comparator comparebyLastNameThenFirstNameWithNull = null; 155 | //BEGINREMOVE 156 | Comparator comparebyLastNameThenFirstNameWithNull = 157 | Comparator.nullsLast( 158 | Comparator.comparing(Person::getLastName) 159 | .thenComparing(Person::getFirstName)); 160 | //ENDREMOVE 161 | 162 | assertTrue(comparebyLastNameThenFirstNameWithNull.compare(michael, rod) < 0); 163 | assertTrue(comparebyLastNameThenFirstNameWithNull.compare(paul, paul) == 0); 164 | assertTrue(comparebyLastNameThenFirstNameWithNull.compare(michael, jermaine) > 0); 165 | assertTrue(comparebyLastNameThenFirstNameWithNull.compare(mick, null) < 0); 166 | assertTrue(comparebyLastNameThenFirstNameWithNull.compare(null, mick) > 0); 167 | } 168 | // Hint: 169 | // 170 | // Use the previous comparator and check the static methods of the Comparator interface. 171 | // 172 | 173 | /** 174 | * Write a Comparator that compares two people by age. 175 | * Try to write the comparator so as to avoid boxing of primitives. 176 | */ 177 | @Test 178 | public void comparator07() { 179 | //TODO//Comparator comparebyAge = null; 180 | //BEGINREMOVE 181 | Comparator comparebyAge = Comparator.comparingInt(Person::getAge); 182 | //ENDREMOVE 183 | 184 | assertTrue(comparebyAge.compare(michael, rod) < 0); 185 | assertTrue(comparebyAge.compare(paul, paul) == 0); 186 | assertTrue(comparebyAge.compare(mick, jermaine) > 0); 187 | } 188 | // Hint: 189 | // 190 | // Look for static methods on the Comparator interface that 191 | // have primitive specializations. 192 | // 193 | 194 | /** 195 | * Write a lambda expression that compares two int values and returns an 196 | * int result that is less than, equal to, or greater than zero, like 197 | * a comparator. Watch out for overflow. The Comparator interface takes 198 | * two objects, but in this case we are comparing int primitives, so the 199 | * functional interface we use is IntBinaryOperator. 200 | */ 201 | @Test 202 | public void comparator08() { 203 | //TODO//IntBinaryOperator intCompare = null; 204 | //BEGINREMOVE 205 | IntBinaryOperator intCompare = (a, b) -> (a < b) ? -1 : (a > b) ? 1 : 0; 206 | // Avoid the following as it is susceptible to overflow: 207 | // IntBinaryOperator intCompare = (a, b) -> a - b; 208 | //ENDREMOVE 209 | 210 | assertTrue(intCompare.applyAsInt(0, 1) < 0); 211 | assertTrue(intCompare.applyAsInt(1, 1) == 0); 212 | assertTrue(intCompare.applyAsInt(2, 1) > 0); 213 | assertTrue(intCompare.applyAsInt(Integer.MIN_VALUE, Integer.MAX_VALUE) < 0); 214 | assertTrue(intCompare.applyAsInt(Integer.MAX_VALUE, Integer.MIN_VALUE) > 0); 215 | } 216 | // Hint: 217 | // 218 | // Use a ternary operator (cond ? result1 : result2) instead of subtraction. 219 | // 220 | 221 | /** 222 | * Write a method reference that compares two int values and returns an 223 | * int result that is less than, equal to, or greater than zero, like 224 | * a comparator. 225 | */ 226 | @Test 227 | public void comparator09() { 228 | //TODO//IntBinaryOperator intCompare = null; 229 | //BEGINREMOVE 230 | IntBinaryOperator intCompare = Integer::compare; 231 | //ENDREMOVE 232 | 233 | assertTrue(intCompare.applyAsInt(0, 1) < 0); 234 | assertTrue(intCompare.applyAsInt(1, 1) == 0); 235 | assertTrue(intCompare.applyAsInt(2, 1) > 0); 236 | assertTrue(intCompare.applyAsInt(Integer.MIN_VALUE, Integer.MAX_VALUE) < 0); 237 | assertTrue(intCompare.applyAsInt(Integer.MAX_VALUE, Integer.MIN_VALUE) > 0); 238 | } 239 | // Hint: 240 | // 241 | // Use a method reference to a static method on the Integer class. 242 | // 243 | 244 | interface DoubleToIntBiFunction { 245 | int applyAsInt(double a, double b); 246 | } 247 | 248 | /** 249 | * Write a method reference that compares two double values and returns an 250 | * int result that is less than, equal to, or greater than zero, like 251 | * a comparator. There functional interface that takes two doubles and returns 252 | * an int, so we define one here. Comparing double values introduces 253 | * special cases such NaN. Consider all NaN values to be equal to each other 254 | * and greater than any non-NaN value. 255 | */ 256 | @Test 257 | public void comparator10() { 258 | //TODO//DoubleToIntBiFunction doubleCompare = null; 259 | //BEGINREMOVE 260 | DoubleToIntBiFunction doubleCompare = Double::compare; 261 | //ENDREMOVE 262 | 263 | assertTrue(doubleCompare.applyAsInt(0.0, 1.0) < 0); 264 | assertTrue(doubleCompare.applyAsInt(1.0, 1.0) == 0); 265 | assertTrue(doubleCompare.applyAsInt(2.0, 1.0) > 0); 266 | assertTrue(doubleCompare.applyAsInt(Double.NaN, Double.NaN) == 0); 267 | assertTrue(doubleCompare.applyAsInt(Double.NaN, 0.0) > 0); 268 | assertTrue(doubleCompare.applyAsInt(0.0, Double.NaN) < 0); 269 | } 270 | // Hint: 271 | // 272 | // Use a method reference to a static method on the Double class. 273 | // 274 | } 275 | -------------------------------------------------------------------------------- /LambdaLab/test/solutions/C_DefaultMethods.java: -------------------------------------------------------------------------------- 1 | package solutions; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.TreeMap; 9 | import java.util.stream.Collectors; 10 | 11 | import org.junit.Ignore; 12 | import org.junit.Test; 13 | 14 | import static org.junit.Assert.assertEquals; 15 | import static org.junit.Assert.assertFalse; 16 | import static org.junit.Assert.assertTrue; 17 | 18 | /** 19 | * This set of exercises covers new default methods on 20 | * the Collections and related APIs. 21 | */ 22 | public class C_DefaultMethods { 23 | 24 | /** 25 | * Given a list of StringBuilders, modify each StringBuilder 26 | * in-place by appending the string "new" to each one. 27 | */ 28 | @Test 29 | public void c01_appendNew() { 30 | List sbList = List.of( 31 | new StringBuilder("alfa"), 32 | new StringBuilder("bravo"), 33 | new StringBuilder("charlie")); 34 | 35 | //UNCOMMENT//// TODO write code to modify sbList 36 | //BEGINREMOVE 37 | sbList.forEach(s -> s.append("new")); 38 | //ENDREMOVE 39 | 40 | assertEquals(List.of("alfanew", "bravonew", "charlienew"), 41 | sbList.stream() 42 | .map(StringBuilder::toString) 43 | .collect(Collectors.toList())); 44 | } 45 | // Hint: 46 | // 47 | // Use Iterable.forEach(). 48 | // 49 | 50 | 51 | /** 52 | * Remove the words that have odd lengths from the list. 53 | */ 54 | @Test 55 | public void c02_removeOddLengthWords() { 56 | List list = new ArrayList<>(Arrays.asList( 57 | "alfa", "bravo", "charlie", "delta", "echo", "foxtrot")); 58 | 59 | //UNCOMMENT//// TODO write code to modify list 60 | //BEGINREMOVE 61 | list.removeIf(s -> (s.length() & 1) == 1); 62 | // Alternatively: 63 | // list.removeIf(s -> (s.length() % 2) != 0); 64 | //ENDREMOVE 65 | 66 | assertEquals(List.of("alfa", "echo"), list); 67 | } 68 | // Hint: 69 | // 70 | // Use Collection.removeIf(). 71 | // 72 | 73 | 74 | /** 75 | * Replace every word in the list with its upper case equivalent. 76 | */ 77 | @Test 78 | public void c03_upcaseAllWords() { 79 | List list = Arrays.asList( 80 | "alfa", "bravo", "charlie", "delta", "echo", "foxtrot"); 81 | 82 | //UNCOMMENT//// TODO code to modify list 83 | //BEGINREMOVE 84 | list.replaceAll(String::toUpperCase); 85 | // Alternatively: 86 | // list.replaceAll(s -> s.toUpperCase()); 87 | //ENDREMOVE 88 | 89 | assertEquals(List.of("ALFA", "BRAVO", "CHARLIE", "DELTA", "ECHO", "FOXTROT"), 90 | list); 91 | } 92 | // Hint: 93 | // 94 | // Use List.replaceAll(). 95 | // 96 | 97 | 98 | /** 99 | * Given a map whose keys are Integers and whose values are StringBuilders, 100 | * append to each StringBuilder the string representation of its corresponding 101 | * Integer key. This should mutate each StringBuilder value in-place. 102 | */ 103 | @Test 104 | public void c04_appendToMapValues() { 105 | Map map = new TreeMap<>(); 106 | map.put(1, new StringBuilder("alfa")); 107 | map.put(2, new StringBuilder("bravo")); 108 | map.put(3, new StringBuilder("charlie")); 109 | 110 | //UNCOMMENT//// TODO write code to modify map 111 | //BEGINREMOVE 112 | map.forEach((k, v) -> v.append(k)); 113 | //ENDREMOVE 114 | 115 | assertEquals(3, map.size()); 116 | assertTrue(map.values().stream().allMatch(x -> x instanceof StringBuilder)); 117 | assertEquals("alfa1", map.get(1).toString()); 118 | assertEquals("bravo2", map.get(2).toString()); 119 | assertEquals("charlie3", map.get(3).toString()); 120 | } 121 | // Hint: 122 | // 123 | // Use Map.forEach(). 124 | // 125 | 126 | 127 | /** 128 | * Given a map whose keys are Integers and whose values are Strings, 129 | * append to each String the string representation of its corresponding 130 | * Integer key. 131 | */ 132 | @Test 133 | public void c05_replaceMapValues() { 134 | Map map = new TreeMap<>(); 135 | map.put(1, "alfa"); 136 | map.put(2, "bravo"); 137 | map.put(3, "charlie"); 138 | 139 | //UNCOMMENT//// TODO write code to modify map 140 | //BEGINREMOVE 141 | map.replaceAll((key, value) -> value + key); 142 | //ENDREMOVE 143 | 144 | assertEquals(Map.of(1, "alfa1", 145 | 2, "bravo2", 146 | 3, "charlie3"), 147 | map); 148 | } 149 | // Hint: 150 | // 151 | // Use Map.replaceAll(). 152 | // 153 | 154 | 155 | /** 156 | * Given a list of words, populate a map whose keys are the lengths of 157 | * each word, and whose values are list of words with that length. 158 | */ 159 | @Test 160 | public void c06_mapOfListOfStringsByLength() { 161 | List list = List.of( 162 | "aardvark", "bison", "capybara", 163 | "alligator", "bushbaby", "chimpanzee", 164 | "avocet", "bustard", "capuchin"); 165 | Map> result = new TreeMap<>(); 166 | 167 | //UNCOMMENT//// TODO write code to populate result 168 | //BEGINREMOVE 169 | list.forEach(s -> result.computeIfAbsent(s.length(), key -> new ArrayList<>()).add(s)); 170 | //ENDREMOVE 171 | 172 | assertEquals(Map.of( 5, List.of("bison"), 173 | 6, List.of("avocet"), 174 | 7, List.of("bustard"), 175 | 8, List.of("aardvark", "capybara", "bushbaby", "capuchin"), 176 | 9, List.of("alligator"), 177 | 10, List.of("chimpanzee")), 178 | result); 179 | } 180 | // 181 | // Use Map.computeIfAbsent() within Iterable.forEach(). 182 | // 183 | 184 | /** 185 | * Given a list of words, populate a map whose keys are the initial characters of 186 | * each word, and whose values are the concatenation of the words with that 187 | * initial character. When concatenating the words, they should be 188 | * separated by a colon (':'). 189 | */ 190 | @Test 191 | public void c07_mapOfStringByInitialCharacter() { 192 | List list = List.of( 193 | "aardvark", "bison", "capybara", 194 | "alligator", "bushbaby", "chimpanzee", 195 | "avocet", "bustard", "capuchin"); 196 | Map result = new TreeMap<>(); 197 | 198 | //UNCOMMENT//// TODO write code to populate result 199 | //BEGINREMOVE 200 | list.forEach(s -> result.merge(s.charAt(0), s, (s1, s2) -> s1 + ":" + s2)); 201 | //ENDREMOVE 202 | 203 | assertEquals(Map.of('a', "aardvark:alligator:avocet", 204 | 'b', "bison:bushbaby:bustard", 205 | 'c', "capybara:chimpanzee:capuchin"), 206 | result); 207 | } 208 | // Hint: 209 | // 210 | // Use Map.merge() within Iterable.forEach(). 211 | // 212 | 213 | 214 | /** 215 | * For some reason the provided map doesn't have mappings for all the keys. This 216 | * is a problem, because if we call get() on a key that isn't present, it returns 217 | * null, and we need to add checks to protect against NullPointerException. 218 | * Write code to ensure that all missing keys are mapped to the empty string. 219 | */ 220 | @Test 221 | public void c08_mapWithMissingValues() { 222 | List keys = Arrays.asList("a", "b", "c", "d", "e", "f", "g"); 223 | Map map = new HashMap<>(Map.of("a", "alfa", 224 | "b", "bravo", 225 | "c", "charlie", 226 | "d", "delta")); 227 | 228 | //UNCOMMENT//// TODO write code to fix the map 229 | //BEGINREMOVE 230 | keys.forEach(key -> map.putIfAbsent(key, "")); 231 | //ENDREMOVE 232 | 233 | assertEquals(Map.of("a", "alfa", 234 | "b", "bravo", 235 | "c", "charlie", 236 | "d", "delta", 237 | "e", "", 238 | "f", "", 239 | "g", ""), 240 | map); 241 | } 242 | // Hint: 243 | // 244 | // Check the Map.putIfAbsent() default method. 245 | // 246 | 247 | 248 | /** 249 | * In the previous example, we added map entries that had a default value. 250 | * We've now determined that's incorrect, and we want to undo that. This 251 | * time, we want to remove the entry if the value is the empty string. 252 | */ 253 | @Test 254 | public void c09_mapRemoveEntriesWithEmptyValues() { 255 | List keys = Arrays.asList("a", "b", "c", "d", "e", "f", "g"); 256 | Map map = new HashMap<>(Map.of("a", "alfa", 257 | "b", "bravo", 258 | "c", "charlie", 259 | "d", "delta", 260 | "e", "", 261 | "f", "", 262 | "g", "")); 263 | 264 | //UNCOMMENT//// TODO write code to fix the map 265 | //BEGINREMOVE 266 | keys.forEach(key -> map.remove(key, "")); 267 | //ENDREMOVE 268 | 269 | assertEquals(Map.of("a", "alfa", 270 | "b", "bravo", 271 | "c", "charlie", 272 | "d", "delta"), 273 | map); 274 | } 275 | // Hint: 276 | // 277 | // Check the two-arg Map.remove() default method. 278 | // 279 | 280 | 281 | /** 282 | * We need another way to deal with the problem of the previous example. 283 | * Instead of removing entries whose value is the empty string, we want 284 | * to replace the empty-string values with a value that's the key itself. 285 | * Write the code to do that. 286 | */ 287 | @Test 288 | public void c10_mapReplaceEmptyValues() { 289 | List keys = Arrays.asList("a", "b", "c", "d", "e", "f", "g"); 290 | Map map = new HashMap<>(Map.of("a", "alfa", 291 | "b", "bravo", 292 | "c", "charlie", 293 | "d", "delta", 294 | "e", "", 295 | "f", "", 296 | "g", "")); 297 | 298 | //UNCOMMENT//// TODO write code to fix the map 299 | //BEGINREMOVE 300 | keys.forEach(key -> map.replace(key, "", key)); 301 | //ENDREMOVE 302 | 303 | assertEquals(Map.of("a", "alfa", 304 | "b", "bravo", 305 | "c", "charlie", 306 | "d", "delta", 307 | "e", "e", 308 | "f", "f", 309 | "g", "g"), 310 | map); 311 | } 312 | // Hint: 313 | // 314 | // Check the Map.replace() default method that takes 3 arguments. 315 | // 316 | 317 | 318 | /** 319 | * We are still dealing with a map with missing entries. For entries that 320 | * are present, we want to convert the value to upper case; and for keys 321 | * that are not present, we want to add an entry where the value is the 322 | * same as the key. 323 | */ 324 | @Test 325 | public void c11_computeWithMissingEntries() { 326 | List keys = Arrays.asList("a", "b", "c", "d", "e", "f", "g"); 327 | Map map = new HashMap<>(Map.of("a", "alfa", 328 | "b", "bravo", 329 | "c", "charlie", 330 | "d", "delta")); 331 | 332 | //UNCOMMENT//// TODO write code transform the map 333 | //BEGINREMOVE 334 | keys.forEach(key -> map.compute(key, (k, v) -> v == null ? k : v.toUpperCase())); 335 | //ENDREMOVE 336 | 337 | assertEquals(Map.of("a", "ALFA", 338 | "b", "BRAVO", 339 | "c", "CHARLIE", 340 | "d", "DELTA", 341 | "e", "e", 342 | "f", "f", 343 | "g", "g"), 344 | map); 345 | } 346 | // Hint: 347 | // 348 | // Check the Map.compute() default method, and read the Javadoc carefully 349 | // regarding the mappings that aren't present. 350 | // 351 | 352 | 353 | /** 354 | * The map now has several entries, some with valid values, and some 355 | * with values that are the empty string. This time, we want to convert 356 | * the non-empty values to upper case, but we want to remove the entries 357 | * for which the values are the empty string. 358 | */ 359 | @Test 360 | public void c12_computeAndRemoveSomeEntries() { 361 | List keys = Arrays.asList("a", "b", "c", "d", "e", "f", "g"); 362 | Map map = new HashMap<>(Map.of("a", "alfa", 363 | "b", "bravo", 364 | "c", "charlie", 365 | "d", "delta", 366 | "e", "", 367 | "f", "", 368 | "g", "")); 369 | 370 | //UNCOMMENT//// TODO write code transform the map 371 | //BEGINREMOVE 372 | keys.forEach(key -> map.compute(key, (k, v) -> v.isEmpty() ? null : v.toUpperCase())); 373 | //ENDREMOVE 374 | 375 | assertEquals(Map.of("a", "ALFA", 376 | "b", "BRAVO", 377 | "c", "CHARLIE", 378 | "d", "DELTA"), 379 | map); 380 | } 381 | // Hint: 382 | // 383 | // Check the Map.compute() default method, read the Javadoc carefully 384 | // for the handling of null values returned from the function. 385 | // 386 | } -------------------------------------------------------------------------------- /LambdaLab/test/solutions/D_SimpleStreams.java: -------------------------------------------------------------------------------- 1 | package solutions; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.nio.charset.StandardCharsets; 6 | import java.nio.file.Files; 7 | import java.nio.file.Paths; 8 | import java.util.Arrays; 9 | import java.util.Comparator; 10 | import java.util.List; 11 | import java.util.stream.Collectors; 12 | import java.util.stream.IntStream; 13 | 14 | import org.junit.After; 15 | import org.junit.Before; 16 | import org.junit.Ignore; 17 | import org.junit.Test; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | import static org.junit.Assert.assertFalse; 21 | import static org.junit.Assert.assertTrue; 22 | 23 | /** 24 | * This set of exercises covers simple stream pipelines, 25 | * including intermediate operations and basic collectors. 26 | * 27 | * Some of these exercises use a BufferedReader variable 28 | * named "reader" that the test has set up for you. 29 | */ 30 | public class D_SimpleStreams { 31 | /** 32 | * Given a list of words, create an output list that contains 33 | * only the odd-length words, converted to upper case. 34 | */ 35 | @Test 36 | public void d1_upcaseOddLengthWords() { 37 | List input = List.of( 38 | "alfa", "bravo", "charlie", "delta", "echo", "foxtrot"); 39 | 40 | //TODO//List result = null; 41 | //BEGINREMOVE 42 | List result = 43 | input.stream() 44 | .filter(w -> (w.length() & 1) == 1) 45 | .map(String::toUpperCase) 46 | .collect(Collectors.toList()); 47 | // Alternatives: 48 | // Instead of w -> (w.length() & 1) == 1, use w -> (w.length() % 2) != 0 49 | // Instead of String::toUpperCase, use w -> w.toUpperCase() 50 | //ENDREMOVE 51 | 52 | assertEquals(List.of("BRAVO", "CHARLIE", "DELTA", "FOXTROT"), result); 53 | } 54 | // Hint 1: 55 | // 56 | // Use filter() and map(). 57 | // 58 | // Hint 2: 59 | // 60 | // To create the result list, use collect() with one of the 61 | // predefined collectors on the Collectors class. 62 | // 63 | 64 | 65 | /** 66 | * Take the third through fifth words of the list, extract the 67 | * second letter from each, and join them, separated by commas, 68 | * into a single string. Watch for off-by-one errors. 69 | */ 70 | @Test 71 | public void d2_joinStreamRange() { 72 | List input = List.of( 73 | "alfa", "bravo", "charlie", "delta", "echo", "foxtrot"); 74 | 75 | //TODO//String result = ""; 76 | //BEGINREMOVE 77 | String result = 78 | input.stream() 79 | .skip(2) 80 | .limit(3) 81 | .map(word -> word.substring(1, 2)) 82 | .collect(Collectors.joining(",")); 83 | //ENDREMOVE 84 | 85 | assertEquals("h,e,c", result); 86 | } 87 | // Hint 1: 88 | // 89 | // Use Stream.skip() and Stream.limit(). 90 | // 91 | // Hint 2: 92 | // 93 | // Use Collectors.joining(). 94 | // 95 | 96 | 97 | /** 98 | * Count the number of lines in the text file. (Remember to 99 | * use the BufferedReader named "reader" that has already been 100 | * opened for you.) 101 | * 102 | * @throws IOException 103 | */ 104 | @Test 105 | public void d3_countLinesInFile() throws IOException { 106 | //TODO//long count = 0; 107 | //BEGINREMOVE 108 | long count = reader.lines() 109 | .count(); 110 | //ENDREMOVE 111 | 112 | assertEquals(14, count); 113 | } 114 | // Hint 1: 115 | // 116 | // Use BufferedReader.lines() to get a stream of lines. 117 | // 118 | // Hint 2: 119 | // 120 | // Use Stream.count(). 121 | // 122 | 123 | 124 | /** 125 | * Find the length of the longest line in the text file. 126 | * 127 | * @throws IOException 128 | */ 129 | @Test 130 | public void d4_findLengthOfLongestLine() throws IOException { 131 | //TODO//int longestLength = 0; 132 | //BEGINREMOVE 133 | int longestLength = 134 | reader.lines() 135 | .mapToInt(String::length) 136 | .max() 137 | .orElse(0); 138 | //ENDREMOVE 139 | 140 | assertEquals(53, longestLength); 141 | } 142 | // Hint 1: 143 | // 144 | // Use Stream.mapToInt() to convert a stream of objects to an IntStream. 145 | // 146 | // Hint 2: 147 | // 148 | // Look at java.util.OptionalInt to get the result. 149 | // 150 | // Hint 3: 151 | // 152 | // Think about the case where the OptionalInt might be empty 153 | // (that is, where it has no value). 154 | // 155 | 156 | 157 | /** 158 | * Find the longest line in the text file. 159 | * 160 | * @throws IOException 161 | */ 162 | @Test 163 | public void d5_findLongestLine() throws IOException { 164 | //TODO//String longest = null; 165 | //BEGINREMOVE 166 | String longest = 167 | reader.lines() 168 | .max(Comparator.comparingInt(String::length)) 169 | .orElse(""); 170 | // Alternative: 171 | // Instead of Comparator.comparingInt(String::length), one could 172 | // use something like: 173 | // (s1, s2) -> Integer.compare(s1.length(), s2.length()) 174 | //ENDREMOVE 175 | 176 | assertEquals("Feed'st thy light's flame with self-substantial fuel,", longest); 177 | } 178 | // Hint 1: 179 | // 180 | // Use Stream.max() with a Comparator. 181 | // 182 | // Hint 2: 183 | // 184 | // Use static methods on Comparator to help create a Comparator instance. 185 | // 186 | 187 | 188 | /** 189 | * Select the longest words from the input list. That is, select the words 190 | * whose lengths are equal to the maximum word length. 191 | */ 192 | @Test 193 | public void d6_selectLongestWords() { 194 | List input = List.of( 195 | "alfa", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel"); 196 | 197 | //TODO//List result = null; 198 | //BEGINREMOVE 199 | int max = input.stream() 200 | .mapToInt(String::length) 201 | .max() 202 | .orElse(-1); 203 | 204 | List result = input.stream() 205 | .filter(s -> s.length() == max) 206 | .collect(Collectors.toList()); 207 | //ENDREMOVE 208 | 209 | assertEquals(List.of("charlie", "foxtrot"), result); 210 | } 211 | // Hint: 212 | // 213 | // Consider making two passes over the input stream. 214 | // 215 | 216 | /** 217 | * Select the list of words from the input list whose length is greater than 218 | * the word's position in the list (starting from zero) . 219 | */ 220 | @Test 221 | public void d7_selectByLengthAndPosition() { 222 | List input = List.of( 223 | "alfa", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel"); 224 | 225 | //TODO//List result = null; 226 | //BEGINREMOVE 227 | List result = 228 | IntStream.range(0, input.size()) 229 | .filter(pos -> input.get(pos).length() > pos) 230 | .mapToObj(pos -> input.get(pos)) 231 | .collect(Collectors.toList()); 232 | //ENDREMOVE 233 | 234 | assertEquals(List.of("alfa", "bravo", "charlie", "delta", "foxtrot"), result); 235 | } 236 | // Hint: 237 | // 238 | // Instead of a stream of words (Strings), run an IntStream of indexes of 239 | // the input list, using index values to get elements from the input list. 240 | // 241 | 242 | 243 | // ======================================================== 244 | // END OF EXERCISES 245 | // TEST INFRASTRUCTURE IS BELOW 246 | // ======================================================== 247 | 248 | 249 | private BufferedReader reader; 250 | 251 | @Before 252 | public void z_setUpBufferedReader() throws IOException { 253 | reader = Files.newBufferedReader( 254 | Paths.get("SonnetI.txt"), StandardCharsets.UTF_8); 255 | } 256 | 257 | @After 258 | public void z_closeBufferedReader() throws IOException { 259 | reader.close(); 260 | } 261 | 262 | } 263 | -------------------------------------------------------------------------------- /LambdaLab/test/solutions/E_IntermediateStreams.java: -------------------------------------------------------------------------------- 1 | package solutions; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.math.BigInteger; 6 | import java.nio.charset.StandardCharsets; 7 | import java.nio.file.Files; 8 | import java.nio.file.Paths; 9 | import java.util.AbstractCollection; 10 | import java.util.AbstractList; 11 | import java.util.ArrayList; 12 | import java.util.Arrays; 13 | import java.util.Comparator; 14 | import java.util.List; 15 | import java.util.Objects; 16 | import java.util.Random; 17 | import java.util.regex.Pattern; 18 | import java.util.stream.Collectors; 19 | import java.util.stream.LongStream; 20 | import java.util.stream.Stream; 21 | 22 | import org.junit.After; 23 | import org.junit.Before; 24 | import org.junit.Ignore; 25 | import org.junit.Test; 26 | 27 | import static org.junit.Assert.assertEquals; 28 | import static org.junit.Assert.assertFalse; 29 | import static org.junit.Assert.assertTrue; 30 | 31 | /** 32 | * This set of exercises covers more advanced stream operations 33 | * longer stream pipelines, and simple reductions. 34 | */ 35 | public class E_IntermediateStreams { 36 | 37 | /** 38 | * Convert a list of strings into a list of characters. 39 | */ 40 | @Test 41 | public void e1_stringsToCharacters() { 42 | List input = List.of("alfa", "bravo", "charlie"); 43 | 44 | //TODO//List result = null; 45 | //BEGINREMOVE 46 | List result = 47 | input.stream() 48 | .flatMap(word -> word.chars().mapToObj(i -> (char)i)) 49 | .collect(Collectors.toList()); 50 | //ENDREMOVE 51 | 52 | assertEquals("[a, l, f, a, b, r, a, v, o, c, h, a, r, l, i, e]", result.toString()); 53 | assertTrue(result.stream().allMatch(x -> x instanceof Character)); 54 | } 55 | // Hint 1: 56 | // 57 | // Use String.chars() and Stream.flatMap(). 58 | // 59 | // Hint 2: 60 | // 61 | // Pay attention to the return type of String.chars() and boxing conversion. 62 | // 63 | 64 | 65 | /** 66 | * Collect all the words from the text file into a list. 67 | * Use the regular expression Pattern SPLIT_PATTERN to split 68 | * a string into words, and use Pattern.splitAsStream(String) 69 | * to do the splitting. SPLIT_PATTERN is defined at the bottom 70 | * of this file. As before, use the BufferedReader variable 71 | * named "reader" that has been set up for you to read from 72 | * the text file. 73 | * 74 | * @throws IOException 75 | */ 76 | @Test 77 | public void e2_listOfAllWords() throws IOException { 78 | //TODO//List output = null; 79 | //BEGINREMOVE 80 | List output = 81 | reader.lines() 82 | .flatMap(line -> SPLIT_PATTERN.splitAsStream(line)) 83 | .collect(Collectors.toList()); 84 | // Alternatively, Stream.of() can be used instead of Arrays.stream(). 85 | //ENDREMOVE 86 | 87 | assertEquals( 88 | List.of( 89 | "From", "fairest", "creatures", "we", "desire", "increase", 90 | "That", "thereby", "beauty's", "rose", "might", "never", "die", 91 | "But", "as", "the", "riper", "should", "by", "time", "decease", 92 | "His", "tender", "heir", "might", "bear", "his", "memory", 93 | "But", "thou", "contracted", "to", "thine", "own", "bright", "eyes", 94 | "Feed'st", "thy", "light's", "flame", "with", "self", "substantial", "fuel", 95 | "Making", "a", "famine", "where", "abundance", "lies", 96 | "Thy", "self", "thy", "foe", "to", "thy", "sweet", "self", "too", "cruel", 97 | "Thou", "that", "art", "now", "the", "world's", "fresh", "ornament", 98 | "And", "only", "herald", "to", "the", "gaudy", "spring", 99 | "Within", "thine", "own", "bud", "buriest", "thy", "content", 100 | "And", "tender", "churl", "mak'st", "waste", "in", "niggarding", 101 | "Pity", "the", "world", "or", "else", "this", "glutton", "be", 102 | "To", "eat", "the", "world's", "due", "by", "the", "grave", "and", "thee"), 103 | output); 104 | } 105 | // Hint: 106 | // 107 | // Use Stream.flatMap(). 108 | // 109 | 110 | 111 | /** 112 | * Read the words from the text file, and create a list containing the words 113 | * of length 8 or longer, converted to lower case, and sorted alphabetically. 114 | * 115 | * @throws IOException 116 | */ 117 | @Test 118 | public void e3_longLowerCaseSortedWords() throws IOException { 119 | //TODO//List output = null; 120 | //BEGINREMOVE 121 | List output = 122 | reader.lines() 123 | .flatMap(line -> SPLIT_PATTERN.splitAsStream(line)) 124 | .filter(word -> word.length() >= 8) 125 | .map(String::toLowerCase) 126 | .sorted() 127 | .collect(Collectors.toList()); 128 | //ENDREMOVE 129 | 130 | assertEquals( 131 | List.of( 132 | "abundance", "beauty's", "contracted", "creatures", 133 | "increase", "niggarding", "ornament", "substantial"), 134 | output); 135 | } 136 | // Hint: 137 | // 138 | // Use Stream.sorted(). 139 | // 140 | 141 | 142 | /** 143 | * Read the words from the text file, and create a list containing the words 144 | * of length 8 or longer, converted to lower case, and sorted reverse alphabetically. 145 | * (Same as above except for reversed sort order.) 146 | * 147 | * @throws IOException 148 | */ 149 | @Test 150 | public void e4_longLowerCaseReverseSortedWords() throws IOException { 151 | //TODO//List result = null; 152 | //BEGINREMOVE 153 | List result = 154 | reader.lines() 155 | .flatMap(line -> SPLIT_PATTERN.splitAsStream(line)) 156 | .filter(word -> word.length() >= 8) 157 | .map(String::toLowerCase) 158 | .sorted(Comparator.reverseOrder()) 159 | .collect(Collectors.toList()); 160 | //ENDREMOVE 161 | 162 | assertEquals( 163 | List.of( 164 | "substantial", "ornament", "niggarding", "increase", 165 | "creatures", "contracted", "beauty's", "abundance"), 166 | result); 167 | } 168 | // Hint: 169 | // 170 | // Use Comparator.reverseOrder(). 171 | // 172 | 173 | 174 | /** 175 | * Read words from the text file, and sort unique, lower-cased words by length, 176 | * then alphabetically within length, and place the result into an output list. 177 | * 178 | * @throws IOException 179 | */ 180 | @Test 181 | public void e5_sortedLowerCaseDistinctByLengthThenAlphabetically() throws IOException { 182 | //TODO//List result = null; 183 | //BEGINREMOVE 184 | List result = 185 | reader.lines() 186 | .flatMap(line -> SPLIT_PATTERN.splitAsStream(line)) 187 | .map(String::toLowerCase) 188 | .distinct() 189 | .sorted(Comparator.comparingInt(String::length) 190 | .thenComparing(Comparator.naturalOrder())) 191 | .collect(Collectors.toList()); 192 | //ENDREMOVE 193 | 194 | assertEquals( 195 | List.of( 196 | "a", "as", "be", "by", "in", "or", "to", "we", 197 | "and", "art", "bud", "but", "die", "due", "eat", "foe", 198 | "his", "now", "own", "the", "thy", "too", "bear", "else", 199 | "eyes", "from", "fuel", "heir", "lies", "only", 200 | "pity", "rose", "self", "that", "thee", "this", "thou", 201 | "time", "with", "churl", "cruel", "flame", "fresh", "gaudy", 202 | "grave", "might", "never", "riper", "sweet", "thine", 203 | "waste", "where", "world", "bright", "desire", "famine", 204 | "herald", "mak'st", "making", "memory", "should", "spring", 205 | "tender", "within", "buriest", "content", "decease", 206 | "fairest", "feed'st", "glutton", "light's", "thereby", "world's", "beauty's", 207 | "increase", "ornament", "abundance", "creatures", "contracted", "niggarding", 208 | "substantial"), 209 | result); 210 | } 211 | // Hint 1: 212 | // 213 | // Use Stream.distinct(). 214 | // 215 | // Hint 2: 216 | // 217 | // Use Comparator.thenComparing(). 218 | // 219 | 220 | /** 221 | * Compute the value of 21!, that is, 21 factorial. This value is larger than 222 | * Long.MAX_VALUE, so you must use BigInteger. 223 | */ 224 | @Test 225 | public void e6_bigFactorial() { 226 | //TODO//BigInteger result = BigInteger.ONE; 227 | //BEGINREMOVE 228 | BigInteger result = 229 | LongStream.rangeClosed(1, 21) 230 | .mapToObj(BigInteger::valueOf) 231 | .reduce(BigInteger.ONE, BigInteger::multiply); 232 | //ENDREMOVE 233 | 234 | assertEquals(new BigInteger("51090942171709440000"), result); 235 | } 236 | // Hint 1: 237 | // 238 | // Use one of the range methods of LongStream to help create 239 | // the BigInteger instances. 240 | // 241 | // Hint 2: 242 | // 243 | // Use Stream.reduce() to "collapse" all elements of a stream into 244 | // a single value. 245 | // 246 | 247 | 248 | /** 249 | * Get the last word in the text file. 250 | * 251 | * @throws IOException 252 | */ 253 | @Test 254 | public void e7_getLastWord() throws IOException { 255 | //TODO//String result = null; 256 | //BEGINREMOVE 257 | String result = 258 | reader.lines() 259 | .flatMap(line -> SPLIT_PATTERN.splitAsStream(line)) 260 | .reduce((a, b) -> b) 261 | .orElse(""); 262 | //ENDREMOVE 263 | 264 | assertEquals("thee", result); 265 | } 266 | // Hint: 267 | // 268 | // Use Stream.reduce() and think about the order of the arguments. 269 | // 270 | 271 | /** 272 | * Create a list containing ArrayList.class and all its super classes. 273 | */ 274 | @Test 275 | public void e8_selectTheSuperClassesOfArrayList() { 276 | Class origin = ArrayList.class; 277 | 278 | //TODO//List result = null; 279 | //BEGINREMOVE 280 | List> result = Stream.>iterate(origin, Class::getSuperclass) 281 | .takeWhile(Objects::nonNull) 282 | .collect(Collectors.toList()); 283 | //ENDREMOVE 284 | 285 | assertEquals( 286 | List.of(ArrayList.class, AbstractList.class, AbstractCollection.class, Object.class), 287 | result); 288 | } 289 | // Hint: 290 | // 291 | // There is a getSuperClass() method on the Class class. 292 | // Creating a stream of these classes can be made with Stream.iterate(). 293 | // Then you need to close that stream when the current class is null. 294 | // Java 9 added the takeWhile() method on the stream interface. 295 | // 296 | 297 | 298 | /** 299 | * Count the length of a stream dropping the first elements on a predicate. 300 | */ 301 | @Test 302 | public void e9_countTheElementsAfterAPredicate() { 303 | 304 | Random rand = new Random(314L); 305 | Stream stream = Stream.iterate( 306 | "", 307 | (String s) -> { 308 | final int nextInt = rand.nextInt(10); 309 | return (nextInt == 0 && !s.isEmpty()) ? s.substring(0, s.length() - 1) : 310 | (nextInt == 8 || nextInt == 9) ? s + "+" 311 | : s; 312 | }).limit(100); 313 | 314 | //TODO//long count = 0L; 315 | //BEGINREMOVE 316 | long count = stream.dropWhile(s -> s.length() < 3).count(); 317 | //ENDREMOVE 318 | 319 | assertEquals(53, count); 320 | } 321 | // Hint: 322 | // 323 | // Java 9 added the dropWhile() method on the stream interface. 324 | // 325 | 326 | 327 | // ======================================================== 328 | // END OF EXERCISES 329 | // TEST INFRASTRUCTURE IS BELOW 330 | // ======================================================== 331 | 332 | 333 | // Pattern for splitting a string into words 334 | static final Pattern SPLIT_PATTERN = Pattern.compile("[- .:,]+"); 335 | 336 | private BufferedReader reader; 337 | 338 | @Before 339 | public void z_setUpBufferedReader() throws IOException { 340 | reader = Files.newBufferedReader( 341 | Paths.get("SonnetI.txt"), StandardCharsets.UTF_8); 342 | } 343 | 344 | @After 345 | public void z_closeBufferedReader() throws IOException { 346 | reader.close(); 347 | } 348 | 349 | } 350 | -------------------------------------------------------------------------------- /LambdaLab/test/solutions/F_AdvancedStreams.java: -------------------------------------------------------------------------------- 1 | package solutions; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.nio.charset.StandardCharsets; 6 | import java.nio.file.Files; 7 | import java.nio.file.Paths; 8 | import java.util.Arrays; 9 | import java.util.Collections; 10 | import java.util.HashSet; 11 | import java.util.IntSummaryStatistics; 12 | import java.util.List; 13 | import java.util.Map; 14 | import java.util.Random; 15 | import java.util.Set; 16 | import java.util.function.Function; 17 | import java.util.regex.Pattern; 18 | import java.util.stream.Collectors; 19 | import java.util.stream.IntStream; 20 | import java.util.stream.Stream; 21 | 22 | import org.junit.After; 23 | import org.junit.Before; 24 | import org.junit.Ignore; 25 | import org.junit.Test; 26 | 27 | import static java.util.Map.entry; 28 | import static org.junit.Assert.assertEquals; 29 | import static org.junit.Assert.assertFalse; 30 | import static org.junit.Assert.assertTrue; 31 | 32 | /** 33 | * This set of exercises covers advanced stream operations, 34 | * including grouping collectors, composition of collectors, 35 | * and customized collectors. 36 | */ 37 | public class F_AdvancedStreams { 38 | 39 | /** 40 | * Categorize the words from the text file into a map, where the map's key 41 | * is the length of each word, and the value corresponding to a key is a 42 | * list of words of that length. Don't bother with uniqueness or lower- 43 | * casing the words. As before, use the BufferedReader variable named 44 | * "reader" that has been set up for you to read from the text file, and 45 | * use SPLIT_PATTERN for splitting the line into words. 46 | * 47 | * @throws IOException 48 | */ 49 | @Test 50 | public void f1_mapLengthToWordList() throws IOException { 51 | //TODO//Map> result = null; 52 | //BEGINREMOVE 53 | Map> result = 54 | reader.lines() 55 | .flatMap(line -> SPLIT_PATTERN.splitAsStream(line)) 56 | .collect(Collectors.groupingBy(String::length)); 57 | //ENDREMOVE 58 | 59 | assertEquals(10, result.get(7).size()); 60 | assertEquals(Set.of("beauty's", "increase", "ornament"), new HashSet<>(result.get(8))); 61 | assertEquals(Set.of("abundance", "creatures"), new HashSet<>(result.get(9))); 62 | assertEquals(Set.of("contracted", "niggarding"), new HashSet<>(result.get(10))); 63 | assertEquals(List.of("substantial"), result.get(11)); 64 | assertFalse(result.containsKey(12)); 65 | } 66 | // Hint: 67 | // 68 | // Use Collectors.groupingBy(). 69 | // 70 | 71 | 72 | /** 73 | * Categorize the words from the text file into a map, where the map's key 74 | * is the length of each word, and the value corresponding to a key is a 75 | * count of words of that length. Don't bother with uniqueness or lower- 76 | * casing the words. This is the same as the previous exercise except 77 | * the map values are the count of words instead of a list of words. 78 | * 79 | * @throws IOException 80 | */ 81 | @Test 82 | public void f2_mapLengthToWordCount() throws IOException { 83 | //TODO//Map result = null; 84 | //BEGINREMOVE 85 | Map result = 86 | reader.lines() 87 | .flatMap(line -> SPLIT_PATTERN.splitAsStream(line)) 88 | .collect(Collectors.groupingBy(String::length, Collectors.counting())); 89 | //ENDREMOVE 90 | 91 | assertEquals(Map.ofEntries(entry( 1, 1L), 92 | entry( 2, 11L), 93 | entry( 3, 28L), 94 | entry( 4, 21L), 95 | entry( 5, 16L), 96 | entry( 6, 12L), 97 | entry( 7, 10L), 98 | entry( 8, 3L), 99 | entry( 9, 2L), 100 | entry(10, 2L), 101 | entry(11, 1L)), 102 | result); 103 | } 104 | // Hint 1: 105 | // 106 | // Use the overload of Collectors.groupingBy() that has 107 | // a "downstream" parameter. 108 | // 109 | // Hint 2: 110 | // 111 | // Use Collectors.counting(). 112 | // 113 | 114 | 115 | /** 116 | * Gather the words from the text file into a map, accumulating a count of 117 | * the number of occurrences of each word. Don't worry about upper case and 118 | * lower case. Extra challenge: implement two solutions, one that uses 119 | * groupingBy() and the other that uses toMap(). 120 | * 121 | * @throws IOException 122 | */ 123 | @Test 124 | public void f3_wordFrequencies() throws IOException { 125 | //TODO//Map result = null; 126 | //BEGINREMOVE 127 | Map result = 128 | reader.lines() 129 | .flatMap(line -> SPLIT_PATTERN.splitAsStream(line)) 130 | .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())); 131 | // or use word -> word instead of Function.identity() 132 | 133 | // Alternative solution using toMap(): 134 | 135 | // Map map = 136 | // reader.lines() 137 | // .flatMap(line -> Stream.of(line.split(REGEXP))) 138 | // .collect(toMap(Function.identity(), 139 | // w -> 1L, 140 | // Long::sum)); 141 | //ENDREMOVE 142 | 143 | assertEquals(2L, (long)result.get("tender")); 144 | assertEquals(6L, (long)result.get("the")); 145 | assertEquals(1L, (long)result.get("churl")); 146 | assertEquals(2L, (long)result.get("thine")); 147 | assertEquals(1L, (long)result.get("world")); 148 | assertEquals(4L, (long)result.get("thy")); 149 | assertEquals(3L, (long)result.get("self")); 150 | assertFalse(result.containsKey("lambda")); 151 | } 152 | // Hint 1: 153 | // 154 | // For Collectors.groupingBy(), consider that each word needs to be in 155 | // a category of its own, that is, each word is categorized as itself. 156 | // 157 | // Hint 2: 158 | // 159 | // For Collectors.toMap(), the first occurrence of a word should be mapped to 1. 160 | // If two elements of the Stream are generating the same key, you will need to 161 | // provide a merging function. 162 | // 163 | 164 | 165 | /** 166 | * From the words in the text file, create nested maps, where the outer map is a 167 | * map from the first letter of the word to an inner map. (Use a string of length 168 | * one as the key.) The inner map, in turn, is a mapping from the length of the 169 | * word to a list of words with that length. Don't bother with any lowercasing 170 | * or uniquifying of the words. 171 | * 172 | * For example, given the words "foo bar baz bazz foo" the string 173 | * representation of the result would be: 174 | * {b={3=[bar, baz], 4=[bazz]}, f={3=[foo, foo]}} 175 | * 176 | * @throws IOException 177 | */ 178 | @Test 179 | public void f4_nestedMaps() throws IOException { 180 | //TODO//Map>> result = null; 181 | //BEGINREMOVE 182 | Map>> result = 183 | reader.lines() 184 | .flatMap(line -> SPLIT_PATTERN.splitAsStream(line)) 185 | .collect(Collectors.groupingBy(word -> word.substring(0,1), 186 | Collectors.groupingBy(String::length))); 187 | //ENDREMOVE 188 | 189 | assertEquals("[abundance]", result.get("a").get(9).toString()); 190 | assertEquals("[by, be, by]", result.get("b").get(2).toString()); 191 | assertEquals("[flame, fresh]", result.get("f").get(5).toString()); 192 | assertEquals("[gaudy, grave]", result.get("g").get(5).toString()); 193 | assertEquals("[should, spring]", result.get("s").get(6).toString()); 194 | assertEquals("[substantial]", result.get("s").get(11).toString()); 195 | assertEquals("[the, thy, thy, thy, too, the, the, thy, the, the, the]", 196 | result.get("t").get(3).toString()); 197 | assertEquals("[where, waste, world]", result.get("w").get(5).toString()); 198 | } 199 | // Hint 1: 200 | // 201 | // The nested map structure that's desired is the result of applying a 202 | // "downstream" collector that's the same operation as the first-level collector. 203 | // 204 | // Hint 2: 205 | // 206 | // Both collection operations are Collectors.groupingBy(). 207 | // 208 | 209 | 210 | /** 211 | * Given a stream of integers, compute separate sums of the even and odd values 212 | * in this stream. Since the input is a stream, this necessitates making a single 213 | * pass over the input. 214 | */ 215 | @Test 216 | public void f5_separateOddEvenSums() { 217 | IntStream input = new Random(987523).ints(20, 0, 100); 218 | 219 | //TODO//int sumEvens = 0; 220 | //TODO//int sumOdds = 0; 221 | //BEGINREMOVE 222 | 223 | Map sums = 224 | input.boxed() 225 | .collect(Collectors.partitioningBy(i -> (i & 1) == 1, 226 | Collectors.summingInt(i -> i))); 227 | int sumEvens = sums.get(false); 228 | int sumOdds = sums.get(true); 229 | //ENDREMOVE 230 | 231 | assertEquals(516, sumEvens); 232 | assertEquals(614, sumOdds); 233 | } 234 | // Hint 1: 235 | // 236 | // Use Collectors.partitioningBy(). 237 | // 238 | // Hint 2: 239 | // 240 | // The collect(Collector) method we need is defined on Stream, but not on 241 | // IntStream, LongStream or DoubleStream. 242 | // 243 | 244 | 245 | /** 246 | * Given a stream of strings, accumulate (collect) them into the result string 247 | * by inserting the input string at both the beginning and end. For example, given 248 | * input strings "x" and "y" the result should be "yxxy". Note: the input stream 249 | * is a parallel stream, so you MUST write a proper combiner function to get the 250 | * correct result. 251 | */ 252 | @Test 253 | public void f6_insertBeginningAndEnd() { 254 | Stream input = List.of( 255 | "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", 256 | "k", "l", "m", "n", "o", "p", "q", "r", "s", "t") 257 | .parallelStream(); 258 | 259 | //UNCOMMENT//String result = input.collect(null, null, null).toString(); 260 | //UNCOMMENT//// TODO fill in lambda expressions or method references 261 | //UNCOMMENT//// in place of the nulls in the line above. 262 | //BEGINREMOVE 263 | String result = 264 | input.collect(StringBuilder::new, 265 | (sb, s) -> sb.insert(0, s).append(s), 266 | (sb1, sb2) -> { 267 | int half = sb2.length() / 2; 268 | sb1.insert(0, sb2.substring(0, half)); 269 | sb1.append(sb2.substring(half)); 270 | }) 271 | .toString(); 272 | //ENDREMOVE 273 | 274 | assertEquals("tsrqponmlkjihgfedcbaabcdefghijklmnopqrst", result); 275 | } 276 | // Hint 1: 277 | // 278 | // The collector state (that is, the object being accumulated and 279 | // combined) can be a single StringBuilder, which is manipulated 280 | // by lambda expressions in the three-arg form of the collect() method. 281 | // 282 | // Hint 2: 283 | // 284 | // The combiner function must take its second argument and merge 285 | // it into the first argument, mutating the first argument. 286 | // 287 | // Hint 3: 288 | // 289 | // The second argument to the combiner function happens AFTER the first 290 | // argument in encounter order, so the second argument needs to be split 291 | // in half and prepended/appended to the first argument. 292 | // 293 | 294 | /** 295 | * Count the total number of words and the number of distinct, lower case 296 | * words in a stream, in one pass. This exercise uses a helper class 297 | * that defines methods that are called by the Stream.collect() method. 298 | * Your task is to fill in the implementation of the accumulate() and 299 | * combine() methods in the helper class. You don't need to modify the 300 | * test method itself. 301 | * 302 | * The stream is run in parallel, so you must write a combine() method 303 | * that works properly. 304 | */ 305 | static class TotalAndDistinct { 306 | private int count = 0; 307 | private final Set set = new HashSet<>(); 308 | 309 | // rely on implicit no-arg constructor 310 | 311 | void accumulate(String s) { 312 | //UNCOMMENT//// TODO write code to accumulate a single string into this object 313 | //BEGINREMOVE 314 | count++; 315 | set.add(s); 316 | //ENDREMOVE 317 | } 318 | 319 | void combine(TotalAndDistinct other) { 320 | //UNCOMMENT//// TODO write code to combine the other object into this one 321 | //BEGINREMOVE 322 | count += other.count; 323 | set.addAll(other.set); 324 | //ENDREMOVE 325 | } 326 | 327 | int getTotalCount() { return count; } 328 | int getDistinctCount() { return set.size(); } 329 | } 330 | // Hint: 331 | // 332 | // The operations you need to write are actually quite simple. 333 | // Don't overthink it. 334 | // 335 | 336 | @Test 337 | public void f7_countTotalAndDistinctWords() { 338 | List allWords = reader.lines() 339 | .map(String::toLowerCase) 340 | .flatMap(line -> SPLIT_PATTERN.splitAsStream(line)) 341 | .collect(Collectors.toList()); 342 | 343 | TotalAndDistinct totalAndDistinct = 344 | Collections.nCopies(100, allWords) 345 | .parallelStream() 346 | .flatMap(List::stream) 347 | .collect(TotalAndDistinct::new, 348 | TotalAndDistinct::accumulate, 349 | TotalAndDistinct::combine); 350 | 351 | assertEquals("distinct count", 81, totalAndDistinct.getDistinctCount()); 352 | assertEquals("total count", 10700, totalAndDistinct.getTotalCount()); 353 | } 354 | 355 | // ======================================================== 356 | // END OF EXERCISES 357 | // TEST INFRASTRUCTURE IS BELOW 358 | // ======================================================== 359 | 360 | 361 | // Pattern for splitting a string into words 362 | static final Pattern SPLIT_PATTERN = Pattern.compile("[- .:,]+"); 363 | 364 | private BufferedReader reader; 365 | 366 | @Before 367 | public void z_setUpBufferedReader() throws IOException { 368 | reader = Files.newBufferedReader( 369 | Paths.get("SonnetI.txt"), StandardCharsets.UTF_8); 370 | } 371 | 372 | @After 373 | public void z_closeBufferedReader() throws IOException { 374 | reader.close(); 375 | } 376 | 377 | } 378 | -------------------------------------------------------------------------------- /LambdaLab/test/solutions/G_MatcherScanner.java: -------------------------------------------------------------------------------- 1 | package solutions; 2 | 3 | import java.io.IOException; 4 | import java.nio.charset.StandardCharsets; 5 | import java.nio.file.Files; 6 | import java.nio.file.Paths; 7 | import java.util.Comparator; 8 | import java.util.regex.Pattern; 9 | import java.util.regex.Matcher; 10 | import java.util.regex.MatchResult; 11 | import java.util.Set; 12 | import java.util.Scanner; 13 | import java.util.stream.Collectors; 14 | 15 | import org.junit.After; 16 | import org.junit.Before; 17 | import org.junit.Ignore; 18 | import org.junit.Test; 19 | 20 | import static org.junit.Assert.assertEquals; 21 | import static org.junit.Assert.assertFalse; 22 | import static org.junit.Assert.assertTrue; 23 | 24 | /** 25 | * JDK 9 added several new streams-producing APIs in the java.util.regex.Matcher 26 | * and java.util.Scanner classes. These APIs make it even easier to process text 27 | * using pattern matching and fluent streams pipelines. The exercises in this file 28 | * use the new JDK 9 pattern matching APIs. 29 | */ 30 | public class G_MatcherScanner { 31 | /** 32 | * Shakespeare used "poetic contractions" which (for purposes of this exercise) 33 | * are words that have an apostrophe (') in a position farther than one letter from 34 | * the end of the word. An example of modern English contraction is "can't" where 35 | * the apostrophe precedes the last letter of the word. An example of a poetic 36 | * contraction is "o'er" (over) where the apostrophe occurs earlier in the word. 37 | * 38 | * Use the Pattern WORD_PAT (defined below) to match words with an apostrophe 39 | * earlier in the word. Match the text from Shakespeare's first sonnet, which has 40 | * been loaded into the String variable SONNET, using the Matcher class, and 41 | * process the results using a Stream. 42 | */ 43 | @Test 44 | public void g1_wordsWithApostrophes() { 45 | //TODO//Set result = null; 46 | //BEGINREMOVE 47 | Set result = WORD_PAT.matcher(SONNET) 48 | .results() 49 | .map(MatchResult::group) 50 | .collect(Collectors.toSet()); 51 | //ENDREMOVE 52 | 53 | assertEquals(Set.of("Feed'st", "mak'st"), result); 54 | } 55 | // Hint: 56 | // 57 | // Use the Matcher.results() method, then convert each MatchResult into a String. 58 | // 59 | 60 | /** 61 | * Perform the same task as in exercise g1, except using Scanner instead of Matcher. 62 | * Use the Scanner class to process the String variable SONNET, matching words as 63 | * described above using WORD_PAT. 64 | */ 65 | @Test 66 | public void g2_wordsWithApostrophes() { 67 | //TODO//Set result = null; 68 | //BEGINREMOVE 69 | Set result = new Scanner(SONNET).findAll(WORD_PAT) 70 | .map(MatchResult::group) 71 | .collect(Collectors.toSet()); 72 | //ENDREMOVE 73 | 74 | assertEquals(Set.of("Feed'st", "mak'st"), result); 75 | } 76 | // Hint: 77 | // 78 | // Use the Scanner.findAll() method, then convert each MatchResult into a String. 79 | // 80 | 81 | /** 82 | * Find all vowel trigraphs (that is, sequences of three consecutive vowels) 83 | * in the string variable SONNET. Replace each matching substring with 84 | * the substring converted to upper case and surrounded by square brackets "[]". 85 | * Use the predefined pattern TRIGRAPH_PAT to match vowel trigraphs. 86 | */ 87 | @Test 88 | public void g3_vowelTrigraphs() { 89 | final Pattern TRIGRAPH_PAT = Pattern.compile("[aeiou]{3}", Pattern.CASE_INSENSITIVE); 90 | //TODO//String result = null; 91 | //BEGINREMOVE 92 | String result = TRIGRAPH_PAT.matcher(SONNET) 93 | .replaceAll(mr -> "[" + mr.group().toUpperCase() + "]"); 94 | //ENDREMOVE 95 | 96 | assertTrue(result.contains("b[EAU]ty's")); 97 | assertEquals(614, result.length()); 98 | } 99 | // Hint: 100 | // 101 | // Use the Matcher.replaceAll() method. 102 | // 103 | 104 | /** 105 | * Use Scanner to parse the SONNET string into whitespace-separated tokens. 106 | * Scanner's default token delimiter is whitespace, so no additional setup 107 | * needs to be done. Then, find the first such token that is of length 10 108 | * or more. 109 | * 110 | * (This task can be performed with String.split() or Pattern.splitAsStream(), 111 | * but the advantage of Scanner is that it operate on a file, an InputStream, 112 | * or a Channel, and all the input need not be loaded into memory.) 113 | */ 114 | @Test 115 | public void g4_firstLongWhitespaceSeparatedToken() { 116 | //TODO//String result = null; 117 | //BEGINREMOVE 118 | String result = new Scanner(SONNET) 119 | .tokens() 120 | .filter(s -> s.length() >= 10) 121 | .findFirst() 122 | .orElseThrow(AssertionError::new); 123 | //ENDREMOVE 124 | 125 | assertEquals("contracted", result); 126 | } 127 | // Hint: 128 | // 129 | // Use the Scanner.tokens() method. 130 | // 131 | 132 | 133 | // ======================================================== 134 | // END OF EXERCISES 135 | // TEST INFRASTRUCTURE IS BELOW 136 | // ======================================================== 137 | 138 | /** 139 | * Pattern for use in exercises g1 and g2. This regex matches a word that contains 140 | * an apostrophe, by matching a word boundary, one or more letters, an apostrophe, 141 | * two or more letters, and a word boundary. Matching is case insensitive. 142 | */ 143 | static final Pattern WORD_PAT = Pattern.compile("\\b[a-z]+'[a-z]{2,}\\b", Pattern.CASE_INSENSITIVE); 144 | 145 | /** 146 | * The text of Shakespeare's first sonnet, in a string. Note, this string 147 | * contains newline characters. 148 | */ 149 | private String SONNET; 150 | 151 | @Before 152 | public void z_readFileIntoString() throws IOException { 153 | SONNET = new String(Files.readAllBytes(Paths.get("SonnetI.txt")), 154 | StandardCharsets.UTF_8); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /LambdaLab/test/solutions/H_Challenges.java: -------------------------------------------------------------------------------- 1 | package solutions; 2 | 3 | import java.io.Serializable; 4 | import java.lang.reflect.Modifier; 5 | import java.util.AbstractMap; 6 | import java.util.ArrayDeque; 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | import java.util.Collection; 10 | import java.util.HashMap; 11 | import java.util.HashSet; 12 | import java.util.LinkedHashSet; 13 | import java.util.List; 14 | import java.util.Map; 15 | import java.util.Objects; 16 | import java.util.OptionalInt; 17 | import java.util.RandomAccess; 18 | import java.util.Set; 19 | import java.util.function.Function; 20 | import java.util.function.IntFunction; 21 | import java.util.function.Predicate; 22 | import java.util.function.Supplier; 23 | import java.util.stream.Collector; 24 | import java.util.stream.Collectors; 25 | import java.util.stream.IntStream; 26 | import java.util.stream.Stream; 27 | 28 | import org.junit.Ignore; 29 | import org.junit.Test; 30 | 31 | import static org.junit.Assert.assertEquals; 32 | import static org.junit.Assert.assertFalse; 33 | import static org.junit.Assert.assertTrue; 34 | 35 | public class H_Challenges { 36 | 37 | /** 38 | * Denormalize this map. The input is a map whose keys are the number of legs of an animal 39 | * and whose values are lists of names of animals. Run through the map and generate a 40 | * "denormalized" list of strings describing the animal, with the animal's name separated 41 | * by a colon from the number of legs it has. The ordering in the output list is not 42 | * considered significant. 43 | * 44 | * Input is Map>: 45 | * { 4=["ibex", "hedgehog", "wombat"], 46 | * 6=["ant", "beetle", "cricket"], 47 | * ... 48 | * } 49 | * 50 | * Output should be a List: 51 | * [ "ibex:4", 52 | * "hedgehog:4", 53 | * "wombat:4", 54 | * "ant:6", 55 | * "beetle:6", 56 | * "cricket:6", 57 | * ... 58 | * ] 59 | */ 60 | @Test 61 | public void h1_denormalizeMap() { 62 | Map> input = new HashMap<>(); 63 | input.put(4, Arrays.asList("ibex", "hedgehog", "wombat")); 64 | input.put(6, Arrays.asList("ant", "beetle", "cricket")); 65 | input.put(8, Arrays.asList("octopus", "spider", "squid")); 66 | input.put(10, Arrays.asList("crab", "lobster", "scorpion")); 67 | input.put(750, Arrays.asList("millipede")); 68 | 69 | //TODO//List result = null; 70 | //BEGINREMOVE 71 | 72 | // Simple solution: use Map.forEach to iterate over each entry, 73 | // and use a nested List.forEach to iterate over each list entry, 74 | // and accumulate values into the result list. 75 | 76 | List result = new ArrayList<>(); 77 | input.forEach((legs, names) -> 78 | names.forEach(name -> result.add(name + ":" + legs))); 79 | 80 | // Alternative solution: stream over map entries, and use flatMap to generate 81 | // Animal instances for each animal name with the given number of legs. This 82 | // is more complicated, but it's a more general technique, and it can be run 83 | // in parallel. 84 | 85 | // List result = 86 | // input.entrySet().stream() 87 | // .flatMap(entry -> entry.getValue().stream() 88 | // .map(name -> name + ":" + entry.getKey())) 89 | // .collect(toList()); 90 | 91 | //ENDREMOVE 92 | 93 | assertEquals(13, result.size()); 94 | assertTrue(result.contains("ibex:4")); 95 | assertTrue(result.contains("hedgehog:4")); 96 | assertTrue(result.contains("wombat:4")); 97 | assertTrue(result.contains("ant:6")); 98 | assertTrue(result.contains("beetle:6")); 99 | assertTrue(result.contains("cricket:6")); 100 | assertTrue(result.contains("octopus:8")); 101 | assertTrue(result.contains("spider:8")); 102 | assertTrue(result.contains("squid:8")); 103 | assertTrue(result.contains("crab:10")); 104 | assertTrue(result.contains("lobster:10")); 105 | assertTrue(result.contains("scorpion:10")); 106 | assertTrue(result.contains("millipede:750")); 107 | } 108 | // Hint 1: 109 | // 110 | // There are several ways to approach this. You could use a stream of map keys, 111 | // a stream of map entries, or nested forEach() methods. 112 | // 113 | // Hint 2: 114 | // 115 | // If you use streams, consider using Stream.flatMap(). 116 | // 117 | 118 | 119 | /** 120 | * Invert a "multi-map". (From an idea by Paul Sandoz) 121 | * 122 | * Given a Map>, convert it to Map>. 123 | * Each set member of the input map's values becomes a key in 124 | * the result map. Each key in the input map becomes a set member 125 | * of the values of the result map. In the input map, an item 126 | * may appear in the value set of multiple keys. In the result 127 | * map, that item will be a key, and its value set will be 128 | * its corresopnding keys from the input map. 129 | * 130 | * In this case the input is Map> 131 | * and the result is Map>. 132 | * 133 | * For example, if the input map is 134 | * {p=[10, 20], q=[20, 30]} 135 | * then the result map should be 136 | * {10=[p], 20=[p, q], 30=[q]} 137 | * irrespective of ordering. Note that the Integer 20 appears 138 | * in the value sets for both p and q in the input map. Therefore, 139 | * in the result map, there should be a mapping with 20 as the key 140 | * and p and q as its value set. 141 | * 142 | * It is possible to accomplish this task using a single stream 143 | * pipeline (not counting nested streams), that is, in a single pass 144 | * over the input, without storing anything in a temporary collection. 145 | */ 146 | @Test 147 | public void h2_invertMultiMap() { 148 | Map> input = new HashMap<>(); 149 | input.put("a", new HashSet<>(Arrays.asList(1, 2))); 150 | input.put("b", new HashSet<>(Arrays.asList(2, 3))); 151 | input.put("c", new HashSet<>(Arrays.asList(1, 3))); 152 | input.put("d", new HashSet<>(Arrays.asList(1, 4))); 153 | input.put("e", new HashSet<>(Arrays.asList(2, 4))); 154 | input.put("f", new HashSet<>(Arrays.asList(3, 4))); 155 | 156 | //TODO//Map> result = null; 157 | //BEGINREMOVE 158 | Map> result = 159 | input.entrySet().stream() 160 | .flatMap(e -> e.getValue().stream() 161 | .map(v -> new AbstractMap.SimpleEntry<>(e.getKey(), v))) 162 | .collect(Collectors.groupingBy(Map.Entry::getValue, 163 | Collectors.mapping(Map.Entry::getKey, 164 | Collectors.toSet()))); 165 | 166 | // Alternatively: 167 | // Map> result = 168 | // input.entrySet().stream() 169 | // .flatMap(e -> e.getValue().stream() 170 | // .map(v -> new AbstractMap.SimpleEntry<>(v, e.getKey()))) 171 | // .collect(Collectors.toMap( 172 | // e -> e.getKey(), 173 | // e -> new HashSet<>(Arrays.asList(e.getValue())), 174 | // (set1, set2) -> { set1.addAll(set2); return set1; } 175 | // )); 176 | 177 | //ENDREMOVE 178 | 179 | assertEquals(new HashSet<>(Arrays.asList("a", "c", "d")), result.get(1)); 180 | assertEquals(new HashSet<>(Arrays.asList("a", "b", "e")), result.get(2)); 181 | assertEquals(new HashSet<>(Arrays.asList("b", "c", "f")), result.get(3)); 182 | assertEquals(new HashSet<>(Arrays.asList("d", "e", "f")), result.get(4)); 183 | assertEquals(4, result.size()); 184 | } 185 | // Hint 1: 186 | // 187 | // A general approach is to flatten the input structure in one stage 188 | // of the pipeline and then to create the result structure using a collector. 189 | // 190 | // Hint 2: 191 | // 192 | // A useful intermediate data structure after the flattening step 193 | // is a pair of items. You can write your own pair class, or you can 194 | // use a pre-existing class like AbstractMap.SimpleEntry. 195 | // 196 | 197 | 198 | /** 199 | * Select the longest words from an input stream. That is, select the words 200 | * whose lengths are equal to the maximum word length. For this exercise, 201 | * you must compute the result in a single pass over the input stream. 202 | * The type of the input is a Stream, so you cannot access elements at random. 203 | * The stream is run in parallel, so the combiner function must be correct. 204 | */ 205 | @Test 206 | public void h3_selectLongestWordsOnePass() { 207 | Stream input = Stream.of( 208 | "alfa", "bravo", "charlie", "delta", 209 | "echo", "foxtrot", "golf", "hotel").parallel(); 210 | 211 | //UNCOMMENT//List result = input.collect( 212 | //UNCOMMENT// Collector.of(null, null, null, null)); 213 | //UNCOMMENT//// TODO implement a collector by replacing the nulls above 214 | //BEGINREMOVE 215 | List result = input.collect( 216 | Collector.of(Longest::new, Longest::acc, Longest::comb, Longest::finish)); 217 | //ENDREMOVE 218 | 219 | assertEquals(Arrays.asList("charlie", "foxtrot"), result); 220 | } 221 | // Hint: 222 | // 223 | // There are several ways to solve this exercise, but one approach is to 224 | // create a helper class with four functions, and then pass method 225 | // references to these functions to the Collector.of() method. 226 | // 227 | 228 | //BEGINREMOVE 229 | static class Longest { 230 | int len = -1; 231 | List list = new ArrayList<>(); 232 | 233 | void acc(String s) { 234 | int slen = s.length(); 235 | if (slen == len) { 236 | list.add(s); 237 | } else if (slen > len) { 238 | len = slen; 239 | list.clear(); 240 | list.add(s); 241 | } // ignore input string if slen < len 242 | } 243 | 244 | Longest comb(Longest other) { 245 | if (this.len > other.len) { 246 | return this; 247 | } else if (this.len < other.len) { 248 | return other; 249 | } else { 250 | this.list.addAll(other.list); 251 | return this; 252 | } 253 | } 254 | 255 | List finish() { 256 | return list; 257 | } 258 | } 259 | //ENDREMOVE 260 | 261 | 262 | /** 263 | * Given a string, split it into a list of strings consisting of 264 | * consecutive characters from the original string. Note: this is 265 | * similar to Python's itertools.groupby function, but it differs 266 | * from Java's Collectors.groupingBy() collector. 267 | */ 268 | @Test 269 | public void h4_splitCharacterRuns() { 270 | String input = "aaaaabbccccdeeeeeeaaafff"; 271 | 272 | //TODO//List result = null; 273 | //BEGINREMOVE 274 | int[] bounds = 275 | IntStream.rangeClosed(0, input.length()) 276 | .filter(i -> i == 0 || i == input.length() || 277 | input.charAt(i-1) != input.charAt(i)) 278 | .toArray(); 279 | 280 | List result = 281 | IntStream.range(1, bounds.length) 282 | .mapToObj(i -> input.substring(bounds[i-1], bounds[i])) 283 | .collect(Collectors.toList()); 284 | //ENDREMOVE 285 | 286 | assertEquals("[aaaaa, bb, cccc, d, eeeeee, aaa, fff]", result.toString()); 287 | } 288 | // Hint: 289 | // 290 | // One possibility is a two-pass approach: one pass to gather data about 291 | // the boundaries between the runs, and the second to create the substrings 292 | // based on output from the first. 293 | // 294 | 295 | /** 296 | * Given a parallel stream of strings, collect them into a collection in reverse order. 297 | * Since the stream is parallel, you MUST write a proper combiner function in order to get 298 | * the correct result. 299 | */ 300 | @Test 301 | public void h5_reversingCollector() { 302 | Stream input = 303 | IntStream.range(0, 100).mapToObj(String::valueOf).parallel(); 304 | 305 | //UNCOMMENT//Collection result = 306 | //UNCOMMENT// input.collect(Collector.of(null, null, null)); 307 | //UNCOMMENT// // TODO fill in collector functions above 308 | //BEGINREMOVE 309 | Collection result = 310 | input.collect(Collector.of(ArrayDeque::new, 311 | ArrayDeque::addFirst, 312 | (d1, d2) -> { d2.addAll(d1); return d2; })); 313 | //ENDREMOVE 314 | 315 | assertEquals( 316 | IntStream.range(0, 100) 317 | .map(i -> 99 - i) 318 | .mapToObj(String::valueOf) 319 | .collect(Collectors.toList()), 320 | new ArrayList<>(result)); 321 | } 322 | // Hint 1: 323 | // 324 | // ArrayDeque supports fast insertion at the front. 325 | // 326 | // Hint 2: 327 | // 328 | // Be careful with ordering of the arguments and results in the combiner. 329 | // 330 | 331 | /** 332 | * Given an array of int, find the int value that occurs a majority 333 | * of times in the array (that is, strictly more than half of the 334 | * elements are that value), and return that int value in an OptionalInt. 335 | * Note, return the majority int value, not the number of times it occurs. 336 | * If there is no majority value, return an empty OptionalInt. 337 | * 338 | * For example, given an input array [11, 12, 12] the result should be 339 | * an OptionalInt containing 12. Given an input array [11, 12, 13] 340 | * the result should be an empty OptionalInt. 341 | */ 342 | 343 | OptionalInt majority(int[] array) { 344 | //TODO//return null; 345 | //BEGINREMOVE 346 | Map map = 347 | Arrays.stream(array) 348 | .boxed() 349 | .collect(Collectors.groupingBy(x -> x, 350 | Collectors.counting())); 351 | 352 | return map.entrySet().stream() 353 | .filter(e -> e.getValue() > array.length / 2) 354 | .mapToInt(Map.Entry::getKey) 355 | .findAny(); 356 | //ENDREMOVE 357 | } 358 | // Hint: 359 | // 360 | // A two-pass approach may be called for here: a counting pass 361 | // and a majority-finding pass. 362 | // 363 | 364 | @Test 365 | public void h6_majority() { 366 | int[] array1 = { 13, 13, 24, 35, 24, 24, 35, 24, 24 }; 367 | int[] array2 = { 13, 13, 24, 35, 24, 24, 35, 24 }; 368 | 369 | OptionalInt result1 = majority(array1); 370 | OptionalInt result2 = majority(array2); 371 | 372 | assertEquals(OptionalInt.of(24), result1); 373 | assertFalse(result2.isPresent()); 374 | } 375 | 376 | /** 377 | * Write a method that takes an IntFunction and returns a Supplier. 378 | * An IntFunction takes an int as an argument and returns some object. 379 | * A Supplier takes no arguments and returns some object. The object 380 | * type in this case is a Shoe that has a single attribute, its size. 381 | * The goal is to write a lambda expression that uses the IntFunction 382 | * and size values provided as arguments, and that returns a Supplier 383 | * that embodies both of them. This is an example of a functional 384 | * programming concept called "partial application." 385 | */ 386 | Supplier makeShoeSupplier(IntFunction ifunc, int size) { 387 | //TODO//return null; 388 | //BEGINREMOVE 389 | return () -> ifunc.apply(size); 390 | //ENDREMOVE 391 | } 392 | // Hint: 393 | // 394 | // You don't want to return the result of calling the IntFunction. 395 | // Instead, you want to return a lambda that calls the IntFunction. 396 | // 397 | 398 | static class Shoe { 399 | final int size; 400 | public Shoe(int size) { this.size = size; } 401 | public int hashCode() { return size ^ 0xcafebabe; } 402 | public boolean equals(Object other) { 403 | return (other instanceof Shoe) && this.size == ((Shoe)other).size; 404 | } 405 | } 406 | 407 | @Test 408 | public void h7_shoemaker() { 409 | Supplier sup1 = makeShoeSupplier(Shoe::new, 9); 410 | Supplier sup2 = makeShoeSupplier(Shoe::new, 13); 411 | 412 | Shoe shoe1 = sup1.get(); 413 | Shoe shoe2 = sup1.get(); 414 | Shoe shoe3 = sup2.get(); 415 | Shoe shoe4 = sup2.get(); 416 | 417 | assertTrue(shoe1 != shoe2); 418 | assertTrue(shoe3 != shoe4); 419 | assertEquals(new Shoe(9), shoe1); 420 | assertEquals(shoe1, shoe2); 421 | assertEquals(new Shoe(13), shoe3); 422 | assertEquals(shoe3, shoe4); 423 | } 424 | 425 | /** 426 | * Write a method that extracts all the superclasses of ArrayList and 427 | * their implemented classes. Filter out the abstract classes, then 428 | * create a map with two boolean keys, true is associated to the interfaces 429 | * and false with the concrete classes. 430 | */ 431 | @Test 432 | public void h8_mapOfClassesAndInterfaces() { 433 | 434 | Class origin = ArrayList.class; 435 | //TODO//Map>> result = null; 436 | //BEGINREMOVE 437 | Stream> classesAndInterfaces = 438 | Stream.>iterate(origin, Class::getSuperclass) 439 | .takeWhile(Objects::nonNull) 440 | .flatMap(c -> Stream.of(Stream.of(c), Arrays.stream(c.getInterfaces()))) 441 | .flatMap(Function.identity()); 442 | 443 | Predicate> isConcrete = c -> ! Modifier.isAbstract(c.getModifiers()); 444 | Predicate> isInterface = Class::isInterface; 445 | 446 | Map>> result = 447 | classesAndInterfaces.filter(isInterface.or(isConcrete)) 448 | .collect(Collectors.partitioningBy(isInterface, Collectors.toSet())); 449 | //ENDREMOVE 450 | 451 | assertEquals(Map.of(false, Set.of(ArrayList.class, Object.class), 452 | true, Set.of(List.class, RandomAccess.class, Cloneable.class, 453 | Serializable.class, Collection.class)), 454 | result); 455 | } 456 | // Hint: 457 | // 458 | // The beginning of this challenge begins with the same kind of pattern 459 | // as the E8 intermediate exercise. 460 | // The interfaces are returned in an array, so one can put them in a stream 461 | // using Arrays.stream(). To add the class to that stream, you can also 462 | // use Stream.of() and flatMap the result to have the final stream. 463 | // Writing the filter step is just a matter of creating the right predicate. 464 | // Then the partioningBy collector will build the map. 465 | // 466 | 467 | /** 468 | * Write a method that extracts all the superclasses and 469 | * their implemented classes. Filter out the abstract classes, then 470 | * create a map with two boolean keys, true is associated to the interfaces 471 | * and false with the concrete classes. Do that for the provided classes, and 472 | * arrange the result in a Map with those classes as the keys. 473 | */ 474 | @Test 475 | public void h9_mapOfMapsOfClassesAndInterfaces() { 476 | 477 | List> origin = List.of(ArrayList.class, HashSet.class, LinkedHashSet.class); 478 | //TODO//Map, Map>>> result = null; 479 | //BEGINREMOVE 480 | Function, Stream>> superClasses = 481 | clazz -> Stream.>iterate(clazz, Class::getSuperclass) 482 | .takeWhile(Objects::nonNull); 483 | 484 | Function>, Stream>> classAndInterfaces = 485 | stream -> stream.flatMap(clazz -> Stream.of(Stream.of(clazz), Arrays.stream(clazz.getInterfaces()))) 486 | .flatMap(Function.identity()); 487 | 488 | Function, Stream>> superClassesAndInterfaces = superClasses.andThen(classAndInterfaces); 489 | 490 | Predicate> isConcrete = c -> ! Modifier.isAbstract(c.getModifiers()); 491 | Predicate> isInterface = Class::isInterface; 492 | Predicate> isInterfaceOrConcreteClass = isInterface.or(isConcrete); 493 | 494 | // 1) To understand the algorithm, write out the previous processing as a stream pattern. 495 | // This isn't used directly, but will be converted to a collector below. 496 | Map>> unusedResult = 497 | origin.stream() 498 | .flatMap(superClassesAndInterfaces) 499 | .filter(isInterfaceOrConcreteClass) 500 | .collect(Collectors.partitioningBy(isInterface, 501 | Collectors.toSet())); 502 | 503 | // 2) Convert the processing to a collector 504 | Collector, ?, Map>>> collector = 505 | Collectors.flatMapping(superClassesAndInterfaces, 506 | Collectors.filtering(isInterfaceOrConcreteClass, 507 | Collectors.partitioningBy(isInterface, 508 | Collectors.toSet()))); 509 | 510 | // 3) use it as a downstream collector 511 | Map, Map>>> result = 512 | origin.stream() 513 | .collect(Collectors.groupingBy(Function.identity(), 514 | collector)); 515 | //ENDREMOVE 516 | 517 | assertEquals( 518 | Map.of( 519 | ArrayList.class, 520 | Map.of(false, Set.of(ArrayList.class, Object.class), 521 | true, Set.of(List.class, RandomAccess.class, Cloneable.class, 522 | Serializable.class, Collection.class)), 523 | HashSet.class, 524 | Map.of(false, Set.of(HashSet.class, Object.class), 525 | true, Set.of(Set.class, Cloneable.class, 526 | Serializable.class, Collection.class)), 527 | LinkedHashSet.class, 528 | Map.of(false, Set.of(LinkedHashSet.class, HashSet.class, Object.class), 529 | true, Set.of(Set.class, Cloneable.class, 530 | Serializable.class, Collection.class))), 531 | result); 532 | } 533 | // Hint: 534 | // 535 | // The trick here is to write the whole processing of the previous 536 | // G8 challenge as a single collector. Once this is done, just pass 537 | // this collector as the downstream collector of a groupingBy. 538 | // A filtering collector and a flatMapping collector have been added 539 | // to JDK9. 540 | // 541 | } -------------------------------------------------------------------------------- /LambdaLab/test/solutions/cleanit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | open($inf, '<', "Exercises.java") or die "Can't open Exercises.java\n"; 4 | open($outf, '>', "../exercises/Exercises.java") or die "Can't open output file\n"; 5 | 6 | $removing = 0; 7 | while (<$inf>) { 8 | if (m,//BEGINREMOVE,) { 9 | $removing = 1; 10 | } 11 | 12 | if (not $removing) { 13 | s/package solutions;/package exercises;/; 14 | s/\@Test/\@Test \@Ignore/; 15 | s,//UNCOMMENT//,,; 16 | print $outf $_; 17 | } 18 | 19 | if (m,//ENDREMOVE,) { 20 | $removing = 0; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /LambdaLab/test/suite/JUnit4TestSuite.java: -------------------------------------------------------------------------------- 1 | package suite; 2 | 3 | import org.junit.runner.RunWith; 4 | import org.junit.runners.Suite; 5 | 6 | @RunWith(Suite.class) 7 | @Suite.SuiteClasses(value={ 8 | exercises.A_Lambdas.class, 9 | exercises.B_Comparators.class, 10 | exercises.C_DefaultMethods.class, 11 | exercises.D_SimpleStreams.class, 12 | exercises.E_IntermediateStreams.class, 13 | exercises.F_AdvancedStreams.class, 14 | exercises.G_MatcherScanner.class, 15 | exercises.H_Challenges.class 16 | }) 17 | public class JUnit4TestSuite { 18 | } 19 | -------------------------------------------------------------------------------- /README.html: -------------------------------------------------------------------------------- 1 |

Oracle Code One Lambda Programming Laboratory

2 | 3 |

Introduction

4 | 5 |

Welcome to the Lambda Programming Laboratory! The goal of this 6 | lab is for you to learn about the lambda expressions, default methods, 7 | and APIs (particularly the Streams library) introduced in Java 8, plus 8 | a few API additions in Java 9 and 10.

9 | 10 |

The lab is structured as a set of exercises in the form of JUnit 11 | tests. To complete each exercise, write some code that uses a 12 | lambda or new API that enables the test to pass.

13 | 14 |

Lab Instructions

15 | 16 |
    17 |
  1. Open the NetBeans development build (or your favorite IDE).

  2. 18 |
  3. Open the LambdaLab project. If you don't have it, it can be cloned 19 | from github:

    20 | 21 |

    git clone https://github.com/stuart-marks/LambdaHOLv2

  4. 22 |
  5. Inside the LambdaLab project, open the Test Packages folder.

  6. 23 |
  7. Open up one of the following exercise files:

    24 | 25 |
    A_Lambdas.java
    26 | B_Comparators.java
    27 | C_DefaultMethods.java
    28 | D_SimpleStreams.java
    29 | E_IntermediateStreams.java
    30 | F_AdvancedStreams.java
    31 | G_MatcherScanner.java
    32 | H_Challenges.java
    33 | 
  8. 34 |
  9. Each exercise is in the form of a single JUnit test method. Each 35 | test is marked with an @Ignore annotation so that JUnit will skip that 36 | test initially.

  10. 37 |
  11. To work on a test, delete the @Ignore annotation, fill in code at 38 | the // TODO marker, trying to avoid modifying any setup code above the 39 | TODO marker and assertion code below the TODO marker.

  12. 40 |
  13. Press Control-F6 to run the tests in this file.

  14. 41 |
  15. Make all the tests pass and get a 100% green bar!

  16. 42 |
43 | 44 |

Detailed Test Description

45 | 46 |

At the top of each exercise is a comment that describes the goal of 47 | the exercise. Within the test method, there is a // TODO comment that 48 | marks the location where you need fill in some implementation 49 | code. There may be some setup code above the // TODO comment, and 50 | there will be some assertion-checking code below. You shouldn't have 51 | to modify any of the setup code at the top of the test method or the 52 | assertions at the bottom of the test method.

53 | 54 |

There is sometimes a hint or two after a test method. If you're having 55 | trouble with an exercise, look for hints. The hint text is inside 56 | a editor fold that is closed by default, so click on the plus-sign 57 | in the left margin to read it.

58 | 59 |

The intent of the exercises is to solve them using the Java 8 lambda 60 | expressions feature, the Java 8 default methods feature, the Java 8 61 | Streams API, and other APIs added in Java 8 or Java 9. Of course, it 62 | is possible to use conventional Java code, such as for-loops, but all 63 | of the exercises are amenable to being solved using new features in 64 | just a handful of lines. Most exercises will take less than half a 65 | dozen lines. Some of the more difficult exercises may take up to eight 66 | lines, depending upon how aggressive you are about breaking 67 | lines. None of the exercises involve writing large amounts of 68 | code. Most of the streams-based exercises involve writing a single 69 | stream pipeline.

70 | 71 |

Several of the exercises read data from a text file. The field named 72 | "reader" is a BufferedReader which will be opened for you on the text 73 | file. In any exercise that refers to reading from the text file, you 74 | can simply use the "reader" variable without worry about opening or 75 | closing it. This is set up by JUnit using the @Before and @After 76 | methods at the bottom of the file. The text file is "SonnetI.txt" 77 | (Shakespeare's first sonnet) which is located at the root of this 78 | NetBeans project.

79 | 80 |

If you're really stuck, the solutions to the exercises are in the package

81 | 82 |
    Test Packages > solutions
83 | 
84 | 85 |

There is one solutions file corresponding to each exercise file. Many 86 | exercises can be solved in several different ways. In some cases, the 87 | solutions file will have several alternatives. Even if you've solved 88 | an exercise, it can be useful to look at the solutions to compare your 89 | solutions with those of the lab authors.

90 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Oracle Code One Lambda Programming Laboratory 2 | 3 | ## Introduction 4 | 5 | Welcome to the Lambda Programming Laboratory! The goal of this 6 | lab is for you to learn about the lambda expressions, default methods, 7 | and APIs (particularly the Streams library) introduced in Java 8, plus 8 | a few API additions in Java 9 and 10. 9 | 10 | The lab is structured as a set of exercises in the form of JUnit 11 | tests. To complete each exercise, write some code that uses a 12 | lambda or new API that enables the test to pass. 13 | 14 | ## Lab Instructions 15 | 16 | 1. Open the NetBeans development build (or your favorite IDE). 17 | 18 | 2. Open the LambdaLab project. If you don't have it, it can be cloned 19 | from github: 20 | 21 | git clone https://github.com/stuart-marks/LambdaHOLv2 22 | 23 | 3. Inside the LambdaLab project, open the Test Packages folder. 24 | 25 | 4. Open up one of the following exercise files: 26 | 27 | A_Lambdas.java 28 | B_Comparators.java 29 | C_DefaultMethods.java 30 | D_SimpleStreams.java 31 | E_IntermediateStreams.java 32 | F_AdvancedStreams.java 33 | G_MatcherScanner.java 34 | H_Challenges.java 35 | 36 | 5. Each exercise is in the form of a single JUnit test method. Each 37 | test is marked with an @Ignore annotation so that JUnit will skip that 38 | test initially. 39 | 40 | 6. To work on a test, delete the @Ignore annotation, fill in code at 41 | the // TODO marker, trying to avoid modifying any setup code above the 42 | TODO marker and assertion code below the TODO marker. 43 | 44 | 7. Press Control-F6 to run the tests in this file. 45 | 46 | 8. Make all the tests pass and get a 100% green bar! 47 | 48 | ## Detailed Test Description 49 | 50 | At the top of each exercise is a comment that describes the goal of 51 | the exercise. Within the test method, there is a // TODO comment that 52 | marks the location where you need fill in some implementation 53 | code. There may be some setup code above the // TODO comment, and 54 | there will be some assertion-checking code below. You shouldn't have 55 | to modify any of the setup code at the top of the test method or the 56 | assertions at the bottom of the test method. 57 | 58 | There is sometimes a hint or two after a test method. If you're having 59 | trouble with an exercise, look for hints. The hint text is inside 60 | a editor fold that is closed by default, so click on the plus-sign 61 | in the left margin to read it. 62 | 63 | The intent of the exercises is to solve them using the Java 8 lambda 64 | expressions feature, the Java 8 default methods feature, the Java 8 65 | Streams API, and other APIs added in Java 8 or Java 9. Of course, it 66 | is possible to use conventional Java code, such as for-loops, but all 67 | of the exercises are amenable to being solved using new features in 68 | just a handful of lines. Most exercises will take less than half a 69 | dozen lines. Some of the more difficult exercises may take up to eight 70 | lines, depending upon how aggressive you are about breaking 71 | lines. None of the exercises involve writing large amounts of 72 | code. Most of the streams-based exercises involve writing a single 73 | stream pipeline. 74 | 75 | Several of the exercises read data from a text file. The field named 76 | "reader" is a BufferedReader which will be opened for you on the text 77 | file. In any exercise that refers to reading from the text file, you 78 | can simply use the "reader" variable without worry about opening or 79 | closing it. This is set up by JUnit using the @Before and @After 80 | methods at the bottom of the file. The text file is "SonnetI.txt" 81 | (Shakespeare's first sonnet) which is located at the root of this 82 | NetBeans project. 83 | 84 | If you're really stuck, the solutions to the exercises are in the package 85 | 86 | Test Packages > solutions 87 | 88 | There is one solutions file corresponding to each exercise file. Many 89 | exercises can be solved in several different ways. In some cases, the 90 | solutions file will have several alternatives. Even if you've solved 91 | an exercise, it can be useful to look at the solutions to compare your 92 | solutions with those of the lab authors. 93 | --------------------------------------------------------------------------------