├── .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