diff --git a/Doc/Sda1/dom.xml b/Doc/Sda1/dom.xml index 956c4aab4e6febe67c177b33109d73e5ec99737b..65f572e780e336eb89c3ec51c030fc2595fc14ff 100644 --- a/Doc/Sda1/dom.xml +++ b/Doc/Sda1/dom.xml @@ -871,48 +871,42 @@ Document contains 15 elements and 3 attributes.</screen> </listitem> </orderedlist> - <para>This exercises idea is:</para> - - <orderedlist> - <listitem> - <para>Create stream based solutions.</para> - </listitem> - - <listitem> - <para>Write a test checking the correctness of each - solution.</para> - </listitem> - </orderedlist> + <para>The project template already contains unit tests. Your + task is providing the implementation generating the expected + results.</para> <para>The first test of <classname>de.hdm_stuttgart.mi.javastreams.Java8FunctionalTest</classname> - is intended to serve as a blueprint:</para> + already being completed is intended to serve as a + blueprint:</para> - <programlisting language="java">/** + <informaltable border="0"> + <tr> + <td valign="top"><programlisting language="java">/** * Order all male students by email and create a List<String> of their respective * names in that order eliminating possible duplicates: <co - linkends="sda1CalloutFunctionalJunit-1" - xml:id="sda1CalloutFunctionalJunit-1-co"/> + linkends="sda1CalloutFunctionalJunit-1" + xml:id="sda1CalloutFunctionalJunit-1-co"/> * * "Bob", 2, Student.Sex.MALE, "bob@uk.edu" <co - linkends="sda1CalloutFunctionalJunit-2" - xml:id="sda1CalloutFunctionalJunit-2-co"/> + linkends="sda1CalloutFunctionalJunit-2" + xml:id="sda1CalloutFunctionalJunit-2-co"/> * "Fred", 2, Student.Sex.MALE, "fred@example.com" * "George", 4, Student.Sex.MALE, "george@math.edu" * "Jane", 1, Student.Sex.FEMALE, "jane@kiv.de" * "Kim", 2, Student.Sex.FEMALE, "wilde@serious.de" * * ==> {"Bob", "Fred", "George"} <co - linkends="sda1CalloutFunctionalJunit-3" - xml:id="sda1CalloutFunctionalJunit-3-co"/> + linkends="sda1CalloutFunctionalJunit-3" + xml:id="sda1CalloutFunctionalJunit-3-co"/> * */ @Test public void allMaleDistinctNameOrderedByEmail() { final List<String> emails = <co - linkends="sda1CalloutFunctionalJunit-4" - xml:id="sda1CalloutFunctionalJunit-4-co"/> + linkends="sda1CalloutFunctionalJunit-4" + xml:id="sda1CalloutFunctionalJunit-4-co"/> roster. stream(). filter(s -> s.gender == Sex.MALE). @@ -922,43 +916,67 @@ public void allMaleDistinctNameOrderedByEmail() { collect(Collectors.toList()); assertThat(emails, <co linkends="sda1CalloutFunctionalJunit-5" - xml:id="sda1CalloutFunctionalJunit-5-co"/> + xml:id="sda1CalloutFunctionalJunit-5-co"/> Matchers.<List<String>> equalTo( ImmutableList.of("Bob", "Fred", "George") ) ); -}</programlisting> +}</programlisting></td> + + <td valign="top"><calloutlist> + <callout arearefs="sda1CalloutFunctionalJunit-1-co" + xml:id="sda1CalloutFunctionalJunit-1"> + <para>An informal description of the desired + outcome.</para> + </callout> + + <callout arearefs="sda1CalloutFunctionalJunit-2-co" + xml:id="sda1CalloutFunctionalJunit-2"> + <para>A list of records illustrating a processing + step.</para> + </callout> + + <callout arearefs="sda1CalloutFunctionalJunit-3-co" + xml:id="sda1CalloutFunctionalJunit-3"> + <para>An informal description of the desired + output.</para> + </callout> + + <callout arearefs="sda1CalloutFunctionalJunit-4-co" + xml:id="sda1CalloutFunctionalJunit-4"> + <para>A stream pipeline yielding the result.</para> + </callout> + + <callout arearefs="sda1CalloutFunctionalJunit-5-co" + xml:id="sda1CalloutFunctionalJunit-5"> + <para>A unit test checking for correctness.</para> + </callout> + </calloutlist></td> + </tr> + </informaltable> - <calloutlist> - <callout arearefs="sda1CalloutFunctionalJunit-1-co" - xml:id="sda1CalloutFunctionalJunit-1"> - <para>An informal description of the desired outcome.</para> - </callout> + <para>A variable like <code language="java">email</code> in the + given example will be set to null in all remaining tests. It is + your task assigning these variables accordingly so that all + tests pass as in:</para> - <callout arearefs="sda1CalloutFunctionalJunit-2-co" - xml:id="sda1CalloutFunctionalJunit-2"> - <para>A list of records illustrating a processing - step.</para> - </callout> + <programlisting language="java">@Test +public void markSumAllStudentsMarksHavingEmail_Dot_de() { - <callout arearefs="sda1CalloutFunctionalJunit-3-co" - xml:id="sda1CalloutFunctionalJunit-3"> - <para>An informal description of the desired output.</para> - </callout> + final int sumMarksOfStudentsHavingGermanEmail = 123; /* TODO: Correct me!*/ - <callout arearefs="sda1CalloutFunctionalJunit-4-co" - xml:id="sda1CalloutFunctionalJunit-4"> - <para>A stream pipeline yielding the result.</para> - </callout> + assertThat(sumMarksOfStudentsHavingGermanEmail, Matchers.equalTo(3)); +}</programlisting> - <callout arearefs="sda1CalloutFunctionalJunit-5-co" - xml:id="sda1CalloutFunctionalJunit-5"> - <para>A unit test checking for correctness.</para> - </callout> - </calloutlist> + <para>Thus the variable <code + language="java">sumMarksOfStudentsHavingGermanEmail</code> + requires an assignment correction based on <code + language="java">students</code> sample data.</para> - <para>Complete the remaining test cases and don't forget to - supply missing tests.</para> + <para>The class + <classname>de.hdm_stuttgart.mi.javastreams.RosterTest</classname> + contains a <methodname>main()</methodname> method also shows + related sample code.</para> <tip> <para>Unit tests for this type of work frequently require diff --git a/P/Sda1/Streams/Solution/src/main/java/de/hdm_stuttgart/mi/javastreams/Student.java b/P/Sda1/Streams/Solution/src/main/java/de/hdm_stuttgart/mi/javastreams/Student.java index 0b506d7c818ec9b8ab35ddb95980b3db08eed12f..9a33aa0043afb2123558ce760819c4544e7bd65a 100644 --- a/P/Sda1/Streams/Solution/src/main/java/de/hdm_stuttgart/mi/javastreams/Student.java +++ b/P/Sda1/Streams/Solution/src/main/java/de/hdm_stuttgart/mi/javastreams/Student.java @@ -1,56 +1,15 @@ package de.hdm_stuttgart.mi.javastreams; /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * - Neither the name of Oracle or the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -import java.util.Arrays; -import java.util.List; + * Loosely derived from https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html. + */ -/** Representing students among with examination marks. */ +/** + * Representing students among with examination marks. + * + */ public class Student { - /** @return Test sample of students. */ - public static List<Student> createRoster() { // Create test data records - - final Student[] roster = new Student[] { - new Student("Fred", 2, Student.Sex.MALE, "fred@example.com") - ,new Student("Jane", 1, Student.Sex.FEMALE, "jane@kiv.de") - ,new Student("George", 4, Student.Sex.MALE, "george@math.edu") - ,new Student("Bob", 2, Student.Sex.MALE, "bob@uk.edu") - ,new Student("Kim", 2, Student.Sex.FEMALE, "wilde@serious.de") - }; - - return Arrays.asList(roster); - } - /** Male or female */ public enum Sex { /** */ diff --git a/P/Sda1/Streams/Solution/src/test/java/de/hdm_stuttgart/mi/javastreams/Java8FunctionalTest.java b/P/Sda1/Streams/Solution/src/test/java/de/hdm_stuttgart/mi/javastreams/Java8FunctionalTest.java index 3b9d0a1d162d80c7e197df226b9c23c77db5b9de..66f30fba8bdcbc54a2573ed5b47cc7d1d3dbf6c3 100644 --- a/P/Sda1/Streams/Solution/src/test/java/de/hdm_stuttgart/mi/javastreams/Java8FunctionalTest.java +++ b/P/Sda1/Streams/Solution/src/test/java/de/hdm_stuttgart/mi/javastreams/Java8FunctionalTest.java @@ -81,19 +81,6 @@ public class Java8FunctionalTest { * <dd>Expected value: 1 + 2 == 3</dd> * </dl> * - * <p>Implementation hint: Process a stream of {@link Student} instances by:</p> - * - * <ol> - * <li>Filter students having a german email.</li> - * <li>Map objects to mark values of type int.</li> - * <li>Provide a suitable - * <a href="https://docs.oracle.com/javase/tutorial/collections/streams/reduction.html#reduce" - * >reduce operation</a>. You may safely assume the existence of at least one student.</li> - * </ol> - * - * <p>You may also want to read - * <a href="https://docs.oracle.com/javase/tutorial/collections/streams/reduction.html#reduce" - * >https://docs.oracle.com/javase/tutorial/collections/streams/reduction.html#reduce</a>.</p> */ @Test public void markSumAllStudentsMarksHavingEmail_Dot_de() { @@ -111,13 +98,6 @@ public class Java8FunctionalTest { * * <p>{"bob@uk.edu, fred@example.com, george@math.edu, ..."}</p> * - * <p>Implementation hint: Process a stream of {@link Student} instances by:</p> - * - * <ol> - * <li>Map {@link Student} instances to their emails.</li> - * <li>Sort alphabetically.</li> - * <li>Use <a href="https://www.baeldung.com/java-8-collectors">a suitable</a> {@link Collector}.</li> - * </ol> */ @Test public void orderedCharacterSeparatedEmailList() { @@ -137,15 +117,6 @@ public class Java8FunctionalTest { * Achieves the same result as in {@link #orderedCharacterSeparatedEmailList()} * internally using <a href="https://www.baeldung.com/java-8-streams#reduction">reduction</a>: * - * <p>Implementation hint: Process a stream of {@link Student} instances by:</p> - * - * <ol> - * <li>Map {@link Student} instances to their emails.</li> - * <li>Sort alphabetically.</li> - * <li>Implement a suitable reducing operation turning a stream of email adresses into - * a single String containing comma separated email adresses.</li> - * <li>You may safely assume at least one email to be present.</li> - * </ol> */ @Test public void orderedCharacterSeparatedEmailList2() { @@ -167,7 +138,6 @@ public class Java8FunctionalTest { * >{"Jane", 1, Student.Sex.FEMALE, "jane@kiv.de"} * {"Kim", 2, Student.Sex.FEMALE, "wilde@serious.de"}</pre> * - * * <p>Female student marks sum: 1 + 2 == 3</p> * * <p>Result: average = 3. / 2 == 1.5</p> diff --git a/P/Sda1/Streams/Template/pom.xml b/P/Sda1/Streams/Template/pom.xml index abe1efe407886d346636df748f153929f184cf5d..31f147722041f9cfa37119d8aae91e3439024234 100644 --- a/P/Sda1/Streams/Template/pom.xml +++ b/P/Sda1/Streams/Template/pom.xml @@ -38,6 +38,12 @@ <version>1.3</version> </dependency> + <dependency> + <groupId>com.github.npathai</groupId> + <artifactId>hamcrest-optional</artifactId> + <version>2.0.0</version> + </dependency> + <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> diff --git a/P/Sda1/Streams/Template/src/main/java/de/hdm_stuttgart/mi/javastreams/RosterTest.java b/P/Sda1/Streams/Template/src/main/java/de/hdm_stuttgart/mi/javastreams/RosterTest.java index 09b4b611cf2b722c5589a656d5fb0ef6e5b4f964..f09a9edc54b4276c736f815d46055adcf6e49a95 100644 --- a/P/Sda1/Streams/Template/src/main/java/de/hdm_stuttgart/mi/javastreams/RosterTest.java +++ b/P/Sda1/Streams/Template/src/main/java/de/hdm_stuttgart/mi/javastreams/RosterTest.java @@ -1,7 +1,6 @@ package de.hdm_stuttgart.mi.javastreams; import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; import java.util.List; import java.util.function.Consumer; @@ -16,248 +15,237 @@ import java.util.function.Predicate; */ public class RosterTest { - static final List<Student> students = ImmutableList.of( - new Student("Fred", 2, Student.Sex.MALE, "fred@example.com") - ,new Student("Jane", 1, Student.Sex.FEMALE, "jane@kiv.de") - ,new Student("George", 4, Student.Sex.MALE, "george@math.edu") - ,new Student("Bob", 2, Student.Sex.MALE, "bob@uk.edu") - ,new Student("Kim", 2, Student.Sex.FEMALE, "wilde@serious.de") - ); + static private final List<Student> students = ImmutableList.of( + new Student("Fred", 2, Student.Sex.MALE, "fred@example.com") + ,new Student("Jane", 1, Student.Sex.FEMALE, "jane@kiv.de") + ,new Student("George", 4, Student.Sex.MALE, "george@math.edu") + ,new Student("Bob", 2, Student.Sex.MALE, "bob@uk.edu") + ,new Student("Kim", 2, Student.Sex.FEMALE, "wilde@serious.de") + ); + + + interface CheckStudent { + boolean test(Student p); + } + + /** + * Approach 1: Create Methods that searching for Persons matching one characteristic + * + * @param mark . + */ + private static void printPersonsOlderThan(final int mark) { + for (final Student p : students) { + if (p.getMark()>= mark) { + System.out.println(p); + } + } + } + + /** + * Approach 2: Create more generalized search methods + * + * @param low Lower mark limit + * @param high upper mark limit + */ + private static void printPersonsWithinMarkRange(final int low, final int high) { + for (final Student p : students) { + if (low <= p.getMark() && p.getMark() <= high) { + System.out.println(p); + } + } + } + + /** + * Approach 3: Specify search criteria in a local class + * Approach 4: Specify search criteria by an anonymous class + * Approach 5: Specify search criteria by lambda expression + * + * @param tester Student instance selector. + */ + private static void printPersons(final CheckStudent tester) { + for (final Student p : students) { + if (tester.test(p)) { + System.out.println(p); + } + } + } + + /** + * Approach 6: Use standard functional interfaces employing lambda expressions. + * + * @param tester Student instance selector. + */ + private static void printPersonsWithPredicate(final Predicate<Student> tester) { + for (final Student p : students) { + if (tester.test(p)) { + System.out.println(p); + } + } + } + + /** + * Approach 7: Use lambda expressions throughout your application + * + * @param tester . + * @param block . + */ + private static void processPersons(final Predicate<Student> tester, + final Consumer<Student> block) { + for (final Student p : students) { + if (tester.test(p)) { + block.accept(p); + } + } + } + + /** + * Approach 7, second example + * + * @param tester . + * @param mapper . + * @param block . + */ + private static void processPersonsByFunction(final Predicate<Student> tester, + final Function<Student, String> mapper, final Consumer<String> block) { + for (final Student p : students) { + if (tester.test(p)) { + String data = mapper.apply(p); + block.accept(data); + } + } + } + + /** + * Approach 8: Use Generics more extensively + * + * @param source . + * @param tester . + * @param mapper . + * @param block . + */ + private static <X, Y> void processElements( final Iterable<X> source, final Predicate<X> tester, + final Function<X, Y> mapper, final Consumer<Y> block) { + for (final X p : source) { + if (tester.test(p)) { + Y data = mapper.apply(p); + block.accept(data); + } + } + } + /** + * @param args unused + */ + public static void main(final String[] args) { - interface CheckStudent { - boolean test(Student p); - } - - /** - * Approach 1: Create Methods that searching for Persons matching one characteristic - * - * @param roster - * @param mark - */ - public static void printPersonsOlderThan(final List<Student> roster, final int mark) { - for (final Student p : roster) { - if (p.getMark()>= mark) { - p.print(); - } - } - } - - /** - * Approach 2: Create more generalized search methods - * - * @param roster - * @param low - * @param high - */ - public static void printPersonsWithinMarkRange(final List<Student> roster, final int low, final int high) { - for (final Student p : roster) { - if (low <= p.getMark() && p.getMark() <= high) { - p.print(); - } - } - } - - /** - * Approach 3: Specify search criteria in a local class - * Approach 4: Specify search criteria by an anonymous class - * Approach 5: Specify search criteria by lambda expression - * - * @param roster - * @param tester - */ - public static void printPersons(final List<Student> roster, final CheckStudent tester) { - for (final Student p : roster) { - if (tester.test(p)) { - p.print(); - } - } - } - - /** - * Approach 6: Use standard functional interfaces employing lambda expressions. - * - * @param roster - * @param tester - */ - public static void printPersonsWithPredicate(final List<Student> roster, final Predicate<Student> tester) { - for (final Student p : roster) { - if (tester.test(p)) { - p.print(); - } - } - } - - /** - * Approach 7: Use lambda expressions throughout your application - * - * @param roster - * @param tester - * @param block - */ - public static void processPersons(final List<Student> roster, final Predicate<Student> tester, - final Consumer<Student> block) { - for (final Student p : roster) { - if (tester.test(p)) { - block.accept(p); - } - } - } - - /** - * Approach 7, second example - * - * @param roster - * @param tester - * @param mapper - * @param block - */ - public static void processPersonsByFunction(final List<Student> roster, final Predicate<Student> tester, - final Function<Student, String> mapper, final Consumer<String> block) { - for (final Student p : roster) { - if (tester.test(p)) { - String data = mapper.apply(p); - block.accept(data); - } - } - } - - /** - * Approach 8: Use Generics more extensively - * - * @param source - * @param tester - * @param mapper - * @param block - */ - public static <X, Y> void processElements( final Iterable<X> source, final Predicate<X> tester, - final Function<X, Y> mapper, final Consumer<Y> block) { - for (final X p : source) { - if (tester.test(p)) { - Y data = mapper.apply(p); - block.accept(data); - } - } - } - - /** - * @param args unused - */ - public static void main(String... args) { - - System.out.println("All student records:"); - for (final Student p : students) { - p.print(); - } - - // Approach 1: Create methods searching for students matching one characteristic - System.out.println("\nApproach 1: Persons older than 20:"); - printPersonsOlderThan(students, 20); - - // Approach 2: Create more generalized search methods - System.out.println("\nApproach 2: Persons between 14 and 30 of age:"); - printPersonsWithinMarkRange(students, 14, 30); - - // Approach 3: Specify search criteria by a local class - System.out.println("\nApproach 3: Persons eligible for selective service:"); - class CheckStudentEligibleForSelectiveService implements CheckStudent { - @Override - public boolean test(Student p) { - return p.getSex() == Student.Sex.MALE - && p.getMark() >= 2 - && p.getMark() <= 3; - } - } - printPersons(students, new CheckStudentEligibleForSelectiveService()); - - // Approach 4: Specify search criteria code in an anonymous class - System.out.println("\nApproach 4: Persons eligible for selective service " + - "(anonymous class):"); - printPersons( - students, - new CheckStudent() { - @Override - public boolean test(final Student p) { - return p.getSex() == Student.Sex.MALE - && p.getMark() >= 2 - && p.getMark() <= 3; - } - } - ); - - // Approach 5: Specify search criteria by lambda expression - System.out.println("\nApproach 5: Persons eligible for selective service " + - "(lambda expression):"); - - printPersons( - students, - (Student p) -> p.getSex() == Student.Sex.MALE - && p.getMark() >= 2 - && p.getMark() <= 3 - ); - - // Approach 6: Use Standard functional interfaces employing lambda expressions - System.out.println("\nApproach 6: Persons eligible for selective service " + - "(with Predicate parameter):"); - - printPersonsWithPredicate( - students, - p -> p.getSex() == Student.Sex.MALE - && p.getMark() >= 18 - && p.getMark() <= 25 - ); - - // Approach 7: Using lamba expressions throughout - System.out.println("\nApproach 7: Persons eligible for selective service " + - "(with Predicate and Consumer parameters):"); - - processPersons( - students, - p -> p.getSex() == Student.Sex.MALE - && p.getMark() >= 2 - && p.getMark() <= 3, - p -> p.print() - ); - - // Approach 7, second example - System.out.println("\nApproach 7, second example: Persons eligible for selective service " + - "(with Predicate, Function, and Consumer parameters):"); - - processPersonsByFunction( - students, - p -> p.getSex() == Student.Sex.MALE - && p.getMark() >= 2 - && p.getMark() <= 3, - p -> p.getEmailAddress(), - email -> System.out.println(email) - ); - - // Approach 8: Use generics more extensively - System.out.println("\nApproach 8: Persons eligible for selective service " + - "(generic version):"); - - processElements( - students, - p -> p.getSex() == Student.Sex.MALE - && p.getMark() >= 2 - && p.getMark() <= 3, - p -> p.getEmailAddress(), - email -> System.out.println(email) - ); - - // Approach 9: Using bulk data operations accepting lambda expressions - // as Parameters - - System.out.println("\nApproach 9: Persons eligible for selective service " + - "(with bulk data operations):"); - - students - .stream() - .filter( - p -> p.getSex() == Student.Sex.MALE - && p.getMark() >= 2 - && p.getMark() <= 3) - .map(p -> p.getEmailAddress()) - .forEach(email -> System.out.println(email)); + System.out.println("All student records:"); + for (final Student p : students) { + System.out.println(p); + } + + // Approach 1: Create methods searching for students matching one characteristic + System.out.println("\nApproach 1: Persons older than 20:"); + printPersonsOlderThan(20); + + // Approach 2: Create more generalized search methods + System.out.println("\nApproach 2: Persons between 14 and 30 of age:"); + printPersonsWithinMarkRange(14, 30); + + // Approach 3: Specify search criteria by a local class + System.out.println("\nApproach 3: Persons eligible for selective service:"); + class CheckStudentEligibleForSelectiveService implements CheckStudent { + @Override + public boolean test(Student p) { + return p.getSex() == Student.Sex.MALE + && p.getMark() >= 2 + && p.getMark() <= 3; + } } + printPersons(new CheckStudentEligibleForSelectiveService()); + + // Approach 4: Specify search criteria code in an anonymous class + System.out.println("\nApproach 4: Persons eligible for selective service " + + "(anonymous class):"); + printPersons( + new CheckStudent() { + @Override + public boolean test(final Student p) { + return p.getSex() == Student.Sex.MALE + && p.getMark() >= 2 + && p.getMark() <= 3; + } + } + ); + + // Approach 5: Specify search criteria by lambda expression + System.out.println("\nApproach 5: Persons eligible for selective service " + + "(lambda expression):"); + + printPersons( + (Student p) -> p.getSex() == Student.Sex.MALE + && p.getMark() >= 2 + && p.getMark() <= 3 + ); + + // Approach 6: Use Standard functional interfaces employing lambda expressions + System.out.println("\nApproach 6: Persons eligible for selective service " + + "(with Predicate parameter):"); + + printPersonsWithPredicate( + p -> p.getSex() == Student.Sex.MALE + && p.getMark() >= 18 + && p.getMark() <= 25 + ); + + // Approach 7: Using lamba expressions throughout + System.out.println("\nApproach 7: Persons eligible for selective service " + + "(with Predicate and Consumer parameters):"); + + processPersons( + p -> p.getSex() == Student.Sex.MALE + && p.getMark() >= 2 + && p.getMark() <= 3, + p -> System.out.println(p) + ); + + // Approach 7, second example + System.out.println("\nApproach 7, second example: Persons eligible for selective service " + + "(with Predicate, Function, and Consumer parameters):"); + + processPersonsByFunction( + p -> p.getSex() == Student.Sex.MALE + && p.getMark() >= 2 + && p.getMark() <= 3, + p -> p.getEmailAddress(), + email -> System.out.println(email) + ); + + // Approach 8: Use generics more extensively + System.out.println("\nApproach 8: Persons eligible for selective service " + + "(generic version):"); + + processElements( + students, + p -> p.getSex() == Student.Sex.MALE + && p.getMark() >= 2 + && p.getMark() <= 3, + p -> p.getEmailAddress(), + email -> System.out.println(email) + ); + + // Approach 9: Using bulk data operations accepting lambda expressions + // as Parameters + + System.out.println("\nApproach 9: Persons eligible for selective service " + + "(with bulk data operations):"); + + students + .stream() + .filter( + p -> p.getSex() == Student.Sex.MALE + && p.getMark() >= 2 + && p.getMark() <= 3) + .map(p -> p.getEmailAddress()) + .forEach(email -> System.out.println(email)); + } } \ No newline at end of file diff --git a/P/Sda1/Streams/Template/src/main/java/de/hdm_stuttgart/mi/javastreams/Student.java b/P/Sda1/Streams/Template/src/main/java/de/hdm_stuttgart/mi/javastreams/Student.java index 6ed4bfd5888c95599904bae649abc88e42b5de52..9f83021574f50945d4c74420e6d04c7b3784c981 100644 --- a/P/Sda1/Streams/Template/src/main/java/de/hdm_stuttgart/mi/javastreams/Student.java +++ b/P/Sda1/Streams/Template/src/main/java/de/hdm_stuttgart/mi/javastreams/Student.java @@ -2,38 +2,41 @@ package de.hdm_stuttgart.mi.javastreams; /* * Loosely derived from https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html. - */ + */ - -/** Representing students among with examination marks. */ +/** + * Representing students among with examination marks. + * + */ public class Student { /** Male or female */ public enum Sex { /** */ - MALE("male"), + MALE("male"), /** */ FEMALE("female"); final String extern; - Sex(final String extern) {this.extern = extern;} + private Sex(final String extern) {this.extern = extern;} + @Override public String toString(){return extern;} } String name; int mark; - Sex sex; + Sex gender; String emailAddress; Student(String name, int mark , - Sex sex, String email) { + Sex gender, String email) { this.name = name; this.mark = mark; - this.sex = sex; + this.gender = gender; this.emailAddress = email; - } + } /** * @return An examination's result */ public int getMark() { @@ -42,7 +45,7 @@ public class Student { /** @return A student's sex. */ public Sex getSex() { - return sex; + return gender; } /** @return A student's name. */ @@ -62,20 +65,12 @@ public class Student { * @return positive if student b's mark is better than student a's, * zero if marks are equal, negative if ... */ - public static int compareByMark(Student a, Student b) { + public static int compareByMark(final Student a, final Student b) { return a.mark - b.mark; } @Override public String toString() { - return getName() + "(" + sex + ", " + getEmailAddress() + ", mark=" + getMark() + ")"; - } - - /** - * Printing all data to standard output. - */ - public void print() { - System.out.println(this); + return getName() + "(" + gender + ", " + getEmailAddress() + ", mark=" + getMark() + ")"; } - } \ No newline at end of file diff --git a/P/Sda1/Streams/Template/src/test/java/de/hdm_stuttgart/mi/javastreams/Java8FunctionalTest.java b/P/Sda1/Streams/Template/src/test/java/de/hdm_stuttgart/mi/javastreams/Java8FunctionalTest.java index 61883b5a790af6407681496470011893399c50d9..90abe1a46f86006b1042bda2bad7b112e3e08a16 100644 --- a/P/Sda1/Streams/Template/src/test/java/de/hdm_stuttgart/mi/javastreams/Java8FunctionalTest.java +++ b/P/Sda1/Streams/Template/src/test/java/de/hdm_stuttgart/mi/javastreams/Java8FunctionalTest.java @@ -2,16 +2,14 @@ package de.hdm_stuttgart.mi.javastreams; import static org.hamcrest.MatcherAssert.assertThat; -import java.util.List; -import java.util.Map; -import java.util.function.Function; +import java.util.*; import java.util.stream.Collector; import java.util.stream.Collectors; +import com.github.npathai.hamcrestopt.OptionalMatchers; import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.FixMethodOrder; -import org.junit.Ignore; import org.junit.Test; import com.google.common.collect.ImmutableList; @@ -23,180 +21,243 @@ import org.junit.runners.MethodSorters; /** * Testing functional queries. */ -@Ignore // Remove me to enable testing @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class Java8FunctionalTest { static private final List<Student> students = ImmutableList.of( new Student("Fred", 2, Student.Sex.MALE, "fred@example.com") - ,new Student("Jane", 1, Student.Sex.FEMALE, "jane@kiv.de") - ,new Student("George", 4, Student.Sex.MALE, "george@math.edu") - ,new Student("Bob", 2, Student.Sex.MALE, "bob@uk.edu") - ,new Student("Kim", 2, Student.Sex.FEMALE, "wilde@serious.de") + ,new Student("Jane", 1, Student.Sex.FEMALE, "jane@kiv.de") + ,new Student("George",4, Student.Sex.MALE, "george@math.edu") + ,new Student("Bob", 2, Student.Sex.MALE, "bob@uk.edu") + ,new Student("Kim", 2, Student.Sex.FEMALE, "wilde@serious.de") ); /** - * Order all male students by email and create a List<String> of their respective - * names alphabetically ordered eliminating possible duplicates: - * - * "Bob", 2, Student.Sex.MALE, "bob@uk.edu" - * "Fred", 2, Student.Sex.MALE, "fred@example.com" - * "George", 4, Student.Sex.MALE, "george@math.edu" - * "Jane", 1, Student.Sex.FEMALE, "jane@kiv.de" - * "Kim", 2, Student.Sex.FEMALE, "wilde@serious.de" - * - * ==> {"Bob", "Fred", "George"} + * <p>Order all male students by email and create a {@code List<String>} of their respective + * names alphabetically ordered eliminating possible duplicates. Implementation hints:</p> + * + * <pre + * >{"Fred", 2, Student.Sex.MALE, "fred@example.com"} + * {"Jane", 1, Student.Sex.FEMALE, "jane@kiv.de"} + * {"George", 4, Student.Sex.MALE, "george@math.edu"} + * {"Bob", 2, Student.Sex.MALE, "bob@uk.edu"} + * {"Kim", 2, Student.Sex.FEMALE, "wilde@serious.de"}</pre> + * + * <p>Result: {"Bob", "Fred", "George"}</p> */ @Test public void allMaleDistinctNameOrderedByEmail() { final List<String> emails = - students. - stream(). - filter(s -> s.sex == Sex.MALE). - map(Student::getName). - sorted(). - distinct(). - collect(Collectors.toList()); + students.parallelStream(). + filter(s -> s.gender == Sex.MALE). + sorted(Comparator.comparing(Student::getEmailAddress)). + map(Student::getName). + distinct(). + collect(Collectors.toList()); assertThat( - emails, - Matchers.equalTo( - ImmutableList.of("Bob", "Fred", "George") - ) - ); + emails, + Matchers.equalTo( + ImmutableList.of("Bob", "Fred", "George") + ) + ); } /** - * Summing up all students' marks having a German email address: - * - * "Jane", 1, Student.Sex.FEMALE, "jane@kiv.de" - * "Kim", 2, Student.Sex.FEMALE, "wilde@serious.de" - * - * Expected value: 1 + 2 == 3 - * - * <p>Implementation hint: Process a stream of Student instances by:</p> + * <p>Summing up all students' marks having a German email address.</p> * - * <ol> - * <li>Filtering students having a german email.</li> - * <li>Map objects to int.</li> - * <li>Provide a suitable reduce operation.</li> + * <dl> + * + * <dt>List of all students having a german E-mail:</dt> + * <dd> + * <ul> + * <li><code>{"Jane", 1, Student.Sex.FEMALE, "jane@kiv.de"}</code></li> + * <li><code>{"Kim", 2, Student.Sex.FEMALE, "wilde@serious.de"}</code></li> + * </ul> + * </dd> + * + * <dt>Summing up related marks:</dt> + * <dd>Expected value: 1 + 2 == 3</dd> + * </dl> + * + * <p>Implementation hint: Process a stream of {@link Student} instances by:</p> * + * <ol> + * <li>Filter students having a german email.</li> + * <li>Map objects to mark values of type int.</li> + * <li>Provide a suitable + * <a href="https://docs.oracle.com/javase/tutorial/collections/streams/reduction.html#reduce" + * >reduce operation</a>. You may safely assume the existence of at least one student.</li> * </ol> * - * <p>You may want to read + * <p>You may also want to read * <a href="https://docs.oracle.com/javase/tutorial/collections/streams/reduction.html#reduce" * >https://docs.oracle.com/javase/tutorial/collections/streams/reduction.html#reduce</a>.</p> */ @Test public void markSumAllStudentsMarksHavingEmail_Dot_de() { - Assert.assertEquals( - 3, //Expected marks sum 3 == 1 + 2 - 0 /* TODO: Start from stream students. ...*/ - ); + + final int sumMarksOfStudentsHavingGermanEmail = 123; /* TODO: Correct me!*/ + + assertThat(sumMarksOfStudentsHavingGermanEmail, Matchers.equalTo(3)); } /** - * A comma separated string containing all students' alphabetically ordered - * email addresses: - * - * "bob@uk.edu, fred@example.com, george@math.edu, ..." - * - * Hint: Read - * http://www.nurkiewicz.com/2014/07/introduction-to-writing-custom.html - * and learn how to write custom collectors. Then implement a string collector - * which uses a {@link StringBuffer} collecting the string elements. Its - * finisher() method may then return the desired result string. + * <p>A comma separated string containing all students' alphabetically ordered email addresses:</p> + * + * <p>{"bob@uk.edu, fred@example.com, george@math.edu, ..."}</p> + * + * <p>Implementation hint: Process a stream of {@link Student} instances by:</p> + * + * <ol> + * <li>Map {@link Student} instances to their emails.</li> + * <li>Sort alphabetically.</li> + * <li>Use <a href="https://www.baeldung.com/java-8-collectors">a suitable</a> {@link Collector}.</li> + * </ol> */ @Test public void orderedCharacterSeparatedEmailList() { - Assert.fail("Implement me!");// TODO + + final String csvSortedEmails = "weirdo!"; /* TODO: Correct me!*/ + + assertThat( + csvSortedEmails, + Matchers.equalTo("bob@uk.edu, fred@example.com, george@math.edu, jane@kiv.de, wilde@serious.de") + ); } - + /** * Achieves the same result as in {@link #orderedCharacterSeparatedEmailList()} - * internally using a standard collector with a modified - * {@link Collector#finisher()} method: - * - * "bob@uk.edu, fred@example.com, george@math.edu, ..." - * - * Hint: Consider {@link Collectors#collectingAndThen(Collector, Function)} and - * read http://www.adam-bien.com/roller/abien/entry/java_8_reducing_a_list. + * internally using <a href="https://www.baeldung.com/java-8-streams#reduction">reduction</a>: + * + * <p>Implementation hint: Process a stream of {@link Student} instances by:</p> + * + * <ol> + * <li>Map {@link Student} instances to their emails.</li> + * <li>Sort alphabetically.</li> + * <li>Implement a suitable reducing operation turning a stream of email adresses into + * a single String containing comma separated email adresses.</li> + * <li>You may safely assume at least one email to be present.</li> + * </ol> */ @Test public void orderedCharacterSeparatedEmailList2() { - Assert.fail("Implement me!");// TODO + + final Optional<String> emailCsvList = Optional.of("Weirdo!"); /* TODO: Correct me!*/ + + assertThat(emailCsvList, OptionalMatchers.isPresentAndIs( + "bob@uk.edu, fred@example.com, george@math.edu, jane@kiv.de, wilde@serious.de")); } /** - * Get the average mark of all female students as double value. - * - * Implementation hint: Map objects to int and provide a suitable reduce operation. - * You may want to read - * https://docs.oracle.com/javase/8/docs/api/java/util/stream/IntStream.html#average-- + * <p>Get the average mark of all female students as double value.</p> + * + * <pre + * >{"Jane", 1, Student.Sex.FEMALE, "jane@kiv.de"} + * {"Kim", 2, Student.Sex.FEMALE, "wilde@serious.de"}</pre> + * + * + * <p>Female student marks sum: 1 + 2 == 3</p> + * + * <p>Result: average = 3. / 2 == 1.5</p> + * */ @Test public void averageMarkFemaleStudents() { - Assert.fail("Implement me!");// TODO + + final OptionalDouble femaleAverage = OptionalDouble.of(23.43); /* TODO: Correct me!*/ + + // No suitable matcher on offer + Assert.assertTrue(femaleAverage.isPresent()); + Assert.assertEquals((1 + 2) / 2., femaleAverage.getAsDouble(), 1.E-20); + } /** - * Create a Map<Student.Sex, List<String>> grouping students by sex and collecting - * their respective names: - * - * Female: - * "Jane" - * "Kim" - * - * Male: - * "Fred" - * "George" - * "Bob" - * - * Implementation hint: - * stackoverflow.com/questions/2509293/map-equality-using-hamcrest#answer-29680356 + * Create a {@code Map<Student.Sex, List<String>>} grouping students by sex and collecting their respective names: + * + * { + * Female: {"Jane", "Kim"}, + * Male: {"Fred", "George", "Bob"} + * } + * */ @Test public void studentNamesBySex() { - final Map<Student.Sex, List<String>> studentnamesBySex = null; // TODO + final Map<Student.Sex, List<String>> studentnamesBySex = new HashMap<>(); /* TODO: Correct me!*/ - assertThat(studentnamesBySex, - Matchers.<Map<Student.Sex, List<String>>> equalTo( - ImmutableMap.of ( - Student.Sex.MALE, ImmutableList.of("Fred", "George", "Bob") - ,Student.Sex.FEMALE, ImmutableList.of("Jane", "Kim") - ) - ) - ); + assertThat( + studentnamesBySex, + Matchers.equalTo( + ImmutableMap.of ( + Student.Sex.MALE, ImmutableList.of("Fred", "George", "Bob"), + Student.Sex.FEMALE, ImmutableList.of("Jane", "Kim") + ) + ) + ); } /** - * Create a Map<Integer, Integer> of student's marks among with their corresponding frequencies: - * + * Marking frequencies: + * * Mark 1: "Jane" --- 1 student - * Mark 2: "Fred", "Bob", "Kim" --> 3 students - * Mark 4: "George" --> 1 student - * + * Mark 2: "Fred", "Bob", "Kim" --- 3 students + * Mark 4: "George" --- 1 student + * + * Result: + * + * <pre>{ + * 1: 1, + * 2: 3, + * 4: 1 + * }</pre> + * + * <p>This one is hard!</p> + * */ @Test public void markingFrequencies() { - Assert.fail("Implement me!");// TODO + final Map<Integer, Integer> frequencyByMark =new HashMap<>(); /* TODO: Correct me!*/ + + assertThat( + frequencyByMark, + Matchers.equalTo( + ImmutableMap.of( + 1, 1, + 2, 3, + 4, 1 + ) + ) + ); } /** - * Students' names in ascending order grouped by by marking category: - * - * Mark 1: {"Jane"} - * Mark 2: {"Bob", "Fred", "Kim"} - * Mark 4: {"George"} - * - * Implementation hint: Sort the stream by student's names beforehand. Then - * apply a suitable - * {@link Collectors#mapping(java.util.function.Function, java.util.stream.Collector)} + * Students' names as {@code List<String>} in ascending order grouped by by mark values: + * <pre>{ + * 1: {"Jane"} + * 2: {"Bob", "Fred", "Kim"} + * 4: {"George"} + * }</pre> + * + * + * <p>This one is even harder!</p> */ @Test - public void studentsByMark() { - Assert.fail("Implement me!");// TODO + public void studentnamesByMark() { + + final Map<Integer, List<String>> namesByMark =new HashMap<>(); /* TODO: Correct me!*/ + + assertThat( + namesByMark, + Matchers.equalTo ( + ImmutableMap.of( + 1, ImmutableList.of("Jane"), + 2, ImmutableList.of("Bob", "Fred", "Kim"), + 4, ImmutableList.of("George") + ) + ) + ); } -} +} \ No newline at end of file