From b5f14567af086a733eaf8039919187b206f72ca2 Mon Sep 17 00:00:00 2001
From: Martin Goik <goik@hdm-stuttgart.de>
Date: Sun, 14 Oct 2018 18:04:56 +0200
Subject: [PATCH] Stream example completed?

---
 Doc/Sda1/dom.xml                              | 116 +++--
 .../hdm_stuttgart/mi/javastreams/Student.java |  53 +-
 .../mi/javastreams/Java8FunctionalTest.java   |  30 --
 P/Sda1/Streams/Template/pom.xml               |   6 +
 .../mi/javastreams/RosterTest.java            | 472 +++++++++---------
 .../hdm_stuttgart/mi/javastreams/Student.java |  35 +-
 .../mi/javastreams/Java8FunctionalTest.java   | 291 ++++++-----
 7 files changed, 500 insertions(+), 503 deletions(-)

diff --git a/Doc/Sda1/dom.xml b/Doc/Sda1/dom.xml
index 956c4aab4..65f572e78 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&lt;String&gt; 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"
  *
  *  ==&gt; {"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&lt;String&gt; emails = <co
-                  linkends="sda1CalloutFunctionalJunit-4"
-                  xml:id="sda1CalloutFunctionalJunit-4-co"/>
+                        linkends="sda1CalloutFunctionalJunit-4"
+                        xml:id="sda1CalloutFunctionalJunit-4-co"/>
             roster.
             stream().
             filter(s -&gt; 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.&lt;List&lt;String&gt;&gt; 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 0b506d7c8..9a33aa004 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 3b9d0a1d1..66f30fba8 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 abe1efe40..31f147722 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 09b4b611c..f09a9edc5 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 6ed4bfd58..9f8302157 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 61883b5a7..90abe1a46 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
-- 
GitLab