Java has additional functionalities since Java 8. It relies heavily on the functional aspect added with lambda expressions and is a big user of Generics.

1. Study Materials

1.1. Tutorials to study

Study the tutorials on Aggregation Operations by Oracle,

and Guide to Java 8 grouingBy on Baeldung.

1.3. Streams, SQL like operations expressed in Java 8

Here too, the package documentation is an interesting read.

A stream can be considered a conveyor belt on which objects experience operations. The belt starts at a source (for instance a Supplier) and can on or more intermediate operations and end with a Terminal operation.

streamops

Here your see a physical stream and a collect operation.

Conveyor and collecting

1.4. sql like functions

select sum(salary)
from   people
where  gender = 'FEMALE'
people.
  stream()                                              (1)
    .filter(p -> p.getGender() == Person.Gender.FEMALE) (2)
    .mapToInt(p -> p.getSalary())                       (3)
    .sum();                                             (4)
1 use people collection as source and start stream
2 where …​ where gender = female
3 mapping is often retrieving the value of a member, get salary, note that it is a Map ToIntFunction<Person> specialised function
4 aggregation, like sum() here is a terminal operation
people.
  stream()
    .filter(p -> p.getGender() == Person.Gender.FEMALE)
    .mapToInt( Person::getSalary)                       (1)
    .sum();
1 This alternative uses a method reference, which can be substituted for a lambda epression when their shapes match.

1.5. Stream explained with paper, pen and LEGO.

Another way to explain a stream application. It is part of the CSVObjectstream idea and design.

stream explained with paper

2. Exercises week 6, Streams.

2.1. Stream starter exercises

In this exercise you work with the simplestream project.

Starting with streams can be a bit confusing. You will have the best experience if you let NetBeans+ the compiler do some of the thinking for you. And then throw in some tricks.

You can consider a pipeline to have two ends: the start, which is the method that typically is called stream() and the end, which is your terminal operation. Often you can start coding by adding the terminal operation from the beginning and insert any intermediate operation between the start and this end of the stream.

count all students.
  int studentcount = students.stream()
  .count();

There will be some example code in the simplestream project in your repository.

We have the following facts on the students.

There are 50 students in total. The number of male students is 16. The youngest student is s Shirleen Simpson, student number 3134539.

Write tests for operations to compute from the given data set:

  • The total number of students. The method is int countStudents.

  • The number of male students. Method int maleCount().

  • The youngest student with Optional<Student> getYoungestStudent(). Think of proper terminal operation.

  • The list of students with a or A in their name (first or last). Use Collectors.toList() to collect the matching students.
    You can either combine two predicates with the proper logical method or concatenate the strings, before you do the test.

  • The list of student than do not have an a or A in their name. Try BOTH variants, using separate method names.

  • The list of students that match a certain criterium, expressed as a Predicate<Student>.
    Construct the predicate and invoke the method with this predicate as parameter. Be imaginative with the functionality.

example predicate
    Predicate<Student> redHaired s -> s.getHairColor() == HairColor.RED;

In all cases use streams and lambdas.

ExtraChallenge EXTRA CHALLENGE

(1) Add a field to the Student class holding of Map of study-topics to grade: Map<String,Double>.
(2) Stream the students and flatMap the students to a stream of grades and compute the overall average of the grades.
(3) Stream the students and flatMap to Map.Entry<String,Double> and collect into a Map of study-topics to average grade.

2.2. CSV to Object Stream

Reading from student disc. Mhw.[1]. or any other kind of object for that matter.

In this task you will write a small utility class that will help you to test things.

The class is a CSVObjectReader and has one constructor and one method.

Of course, you start with tests. To do that, you will find a student.csv file in the NetBeans-IDE project.

The test should:

  • For the constructor call use {@code Factories::createStudent} as creator. From the String "students.csv", create a Path java.nio.file.Path using the Paths utility class in the same package.

  • Start the stream with {@code Student.class} as objectType parameter and use the regular expression (String) {@code "^\\d+$"} to create a lambda expression as the predicate to ensure that the first string in the array of strings contains only digits.

  • Collect the resulting stream in a list and assert

    • The list is not null.

    • The list is not empty.

    • The list has 50 elements.

    • The list contains a student with the name "Olympia Oliphant" at index three.

  • In all the asserts, use a message and make sure you put the arguments to the assertXXX method in the proper order.

From the test you can infer that:

  • The constructor takes two parameters: The type of the object this stream will produce/contain and the filename (a String) from which the content is read. The type of the type parameter is Class<?> type which you can read as a class object of some (yet) unknown type.

The said method is stream() and takes two parameters:

  • a Function, called creator, which it selves accepts (as in consumes) an array of String, and returns a T.

  • a Predicate called rowFilter which accepts (also as in consumes) an array of `String`s and says yes or no to the question: is this string suitable?

The use case of this utility class is to read data from files and turn them into real (Java) objects.

You can use it as is shown in the listing below

2.2.1. Show that it works

To show the use of this class, create a csv file, of take it from an earlier project, and define a type, say student with studentnumber, name, birthdata, and gender.

To top the demonstration off, add a filter function that accepts a list of students, which streams said list and filters out students by some rule, e.g. by gender and or age.

A Helper class called Factories might be useful in your demo. Factories in our solution has two methods, one to create a student from an array of String, as is required by the CSVObjectStream class and one method to turn object back into csv records. We call the last one studentAsCSVLine. For the remaining few details look at the class diagram below.

You may want to keep this project and reuse it in other exercises.

To hand in: NetBeans project with a separate Main file that demonstrates the working of your module.

CSVObjectStream<Student> os
            = new CSVObjectStream( Student.class, Paths.get( "students.csv" ) );

        os.stream( Factories::createStudent,
                    r -> r[ 0 ].matches( "\\d+" ) )
            .forEach( System.out::println );
csvobjectclasses
Figure 1. class diagram csvobjectstream

2.3. Lambda Library

Within this assignment you will get familiar with the syntax of lambda expressions, as well as how to apply them. Furthermore you will use the new streams to get some work with collections done in a smart way.

2.3.1. Lambdas in a Library

In this assignment you will implement the business logic for a library. This library provides some methods to get access to the books and to retrieve several views on that books. A simple swing-GUI which displays the books of the library is given. You can use that to see parts of your implementation in action.

As usually, you will find an NetBeans project for this assignment inserted in you subversion repository. Note that the project will run, but not all functionality is implemented, yet. Proceed in the following steps:

  1. Start working on this assignment by studying the Javadoc which is given. Take especially care of the classes LibraryModel and SearchStrategy, since they will form the requirements you will have to implement.

  2. Implement one of the test cases in the test class LibraryModelTest. A good starting point is the testGetBooks() method.

  3. Implement the method that you tested in the previous step.

  4. Repeat the steps 2 and 3 until all functionality is implemented.

Note that this task has a dependence on the previous task cvsobjects, to read a csv file as book objects.

Additional information on lambda expressions and the java 8 features can be found at the following locations:

javadoc of the application. This provides the requirements of all the methods to be tested and implemented. You can derive your test data from the `library.csv} file.

Further hints

  • To complete this task, study the lambda tutorial. See RealJava website.

  • For the testing part you should also study chapter 6 of Kaczanowski, assuming you read and understood the earlier chapters too.

  • The AssertJ library for testing can be considered the successor of the FEST assertion Library, and that is what we use in this exercise.

  • Use the AssertJ website and documentation at Add AssertJ doc.




1. Terry Pratchett would have enjoyed this one