Java Streams and LocalDate

It is Monday November 09, 2020 and it has been a week since the Election Day and the tally of votes is still being adjusted. What a disaster for the USA and friends.

I am not sure why we are not using a voter ID card and limiting voting in person at the polls on Election Day. No exceptions! I am not implying the obvious, but the changes made to the electoral system this year was an open invitation for fraud. Not sure which part of our government is responsible for making sure elections are legal and trustworthy.

If you disagree with the voter ID card and a secure electronic vote at polling sites I would like to provide you with some facts (not opinions) which seem to be all over the news and social media. When you get a credit card via USPS, for your protection, you are asked to call a toll free number and answer a few questions which would allow the credit company to enable the card so you can start charging on it. Credit card companies / banks do not trust the USPS with money.

When was the last time you put some cash in an envelope and sent it to a friend or family member? Chances are it would not get to its destination. Check here that the USPS has to say about it. Yep, they do not recommend it!

I am sure you have read through the years that electronic voting is not secure. Such news comes from people who have interest in not allowing electronic voting. It is very safe and will stop electoral fraud which at least in these past elections, has been rampant.

Do you have proof of a bank transaction that was tampered with over the network? Issues with credit cards, databases and files are caused not by the encryption mechanism securing the transactions. If so, we would be banking in person. The banks are in the business of making money, not giving it away. The problems are always card skimmers on terminals, cameras, thugs with weapons, databases that were left unprotected in the cloud, inside jobs, etc. NOT ENCRYPTION.

So if you use a very small set of technologies you can have an extremely secured voting system. Of course, the system might not be attractive to people that are interested in election fraud. They will tell Americans that such systems are not as secure as the USPS (and this is not a joke).

Our country needs ACCOUNTABILITY. For example, companies like Facebook, Twitter, should not be allowed to make any changes to posts. It is up to the affected people to have the authorities investigate the truth of the contents and if they comply with the laws. If not, then the authors should be held accountable. The companies should just make sure that all users are properly identified so they can be located and held accountable for their actions. Opinions should be marked in bold letters in any post so we can differentiate them from news.

Enough of this, I just get somewhat disgusted with the lack of accountability in our country and how people and companies are allowed to do things that should not be legal or moral.

Last week I was talking about some of the features (i.e., lambda expressions, LocalDate, and streams to just mention a few) that were introduced into Java 8 a few years ago. Based on that, I decided to write some code using mostly pre-Java 8 features and associated code using Java 8.

Earlier this year I wrote the Java Stream post. This is similar yet different. I tried to concentrate not only on streams but also in LocalDate and lambda expressions.

The code is obviously written in Java so it should execute on most (never generalize) platforms. I used Windows 10 and the VSCode IDE. You should be able to run the code without on most platforms.

main <<< p: (name: Money Penny, gender: FEMALE, birthDay: 1980-05-22)
main <<< p: (name: James Bond, gender: MALE, birthDay: 1970-04-03)
main <<< p: (name: Dink, gender: FEMALE, birthDay: 1965-01-31)
main <<< p: (name: Tatiana Romanova, gender: FEMALE, birthDay: 1982-08-19)
main <<< p: (name: Auric Goldfinger, gender: MALE, birthDay: 1960-08-15)
main <<< p: (name: Honey Rider, gender: FEMALE, birthDay: 1975-02-13)
main <<< p: (name: Julius No, gender: MALE, birthDay: 1965-07-04)
main <<< p: (name: Anya Amasova, gender: FEMALE, birthDay: 1972-06-17)
main <<< p: (name: Domino Derval, gender: FEMALE, birthDay: 1973-09-28)
main <<< p: (name: Aki, gender: FEMALE, birthDay: 1973-11-14)

before0 <<< f: (name: Money Penny, gender: FEMALE, birthDay: 1980-05-22)
before0 <<< f: (name: Dink, gender: FEMALE, birthDay: 1965-01-31)
before0 <<< f: (name: Tatiana Romanova, gender: FEMALE, birthDay: 1982-08-19)
before0 <<< f: (name: Honey Rider, gender: FEMALE, birthDay: 1975-02-13)
before0 <<< f: (name: Anya Amasova, gender: FEMALE, birthDay: 1972-06-17)
before0 <<< f: (name: Domino Derval, gender: FEMALE, birthDay: 1973-09-28)
before0 <<< f: (name: Aki, gender: FEMALE, birthDay: 1973-11-14)

after0 <<< f: (name: Money Penny, gender: FEMALE, birthDay: 1980-05-22)
after0 <<< f: (name: Dink, gender: FEMALE, birthDay: 1965-01-31)
after0 <<< f: (name: Tatiana Romanova, gender: FEMALE, birthDay: 1982-08-19)
after0 <<< f: (name: Honey Rider, gender: FEMALE, birthDay: 1975-02-13)
after0 <<< f: (name: Anya Amasova, gender: FEMALE, birthDay: 1972-06-17)
after0 <<< f: (name: Domino Derval, gender: FEMALE, birthDay: 1973-09-28)
after0 <<< f: (name: Aki, gender: FEMALE, birthDay: 1973-11-14)

before1 <<< person: (name: Aki, gender: FEMALE, birthDay: 1973-11-14)
before1 <<< person: (name: Anya Amasova, gender: FEMALE, birthDay: 1972-06-17)
before1 <<< person: (name: Auric Goldfinger, gender: MALE, birthDay: 1960-08-15)
before1 <<< person: (name: Dink, gender: FEMALE, birthDay: 1965-01-31)
before1 <<< person: (name: Domino Derval, gender: FEMALE, birthDay: 1973-09-28)
before1 <<< person: (name: Honey Rider, gender: FEMALE, birthDay: 1975-02-13)
before1 <<< person: (name: James Bond, gender: MALE, birthDay: 1970-04-03)
before1 <<< person: (name: Julius No, gender: MALE, birthDay: 1965-07-04)
before1 <<< person: (name: Money Penny, gender: FEMALE, birthDay: 1980-05-22)
before1 <<< person: (name: Tatiana Romanova, gender: FEMALE, birthDay: 1982-08-19)

after1 <<< p: (name: Aki, gender: FEMALE, birthDay: 1973-11-14)
after1 <<< p: (name: Anya Amasova, gender: FEMALE, birthDay: 1972-06-17)
after1 <<< p: (name: Auric Goldfinger, gender: MALE, birthDay: 1960-08-15)
after1 <<< p: (name: Dink, gender: FEMALE, birthDay: 1965-01-31)
after1 <<< p: (name: Domino Derval, gender: FEMALE, birthDay: 1973-09-28)
after1 <<< p: (name: Honey Rider, gender: FEMALE, birthDay: 1975-02-13)
after1 <<< p: (name: James Bond, gender: MALE, birthDay: 1970-04-03)
after1 <<< p: (name: Julius No, gender: MALE, birthDay: 1965-07-04)
after1 <<< p: (name: Money Penny, gender: FEMALE, birthDay: 1980-05-22)
after1 <<< p: (name: Tatiana Romanova, gender: FEMALE, birthDay: 1982-08-19)

before2 <<< age: 37 allMatch: true
after2 <<<  age: 37 allMatch: true

before3 <<< age: 61 anyMatch: false
after3 <<<  age: 61 anyMatch: false

before4 <<< person: (name: Money Penny, gender: FEMALE, birthDay: 1980-05-22) age: 40
before4 <<< person: (name: James Bond, gender: MALE, birthDay: 1970-04-03) age: 50
before4 <<< person: (name: Dink, gender: FEMALE, birthDay: 1965-01-31) age: 55
before4 <<< person: (name: Tatiana Romanova, gender: FEMALE, birthDay: 1982-08-19) age: 38
before4 <<< person: (name: Auric Goldfinger, gender: MALE, birthDay: 1960-08-15) age: 60
before4 <<< person: (name: Honey Rider, gender: FEMALE, birthDay: 1975-02-13) age: 45
before4 <<< person: (name: Julius No, gender: MALE, birthDay: 1965-07-04) age: 55
before4 <<< person: (name: Anya Amasova, gender: FEMALE, birthDay: 1972-06-17) age: 48
before4 <<< person: (name: Domino Derval, gender: FEMALE, birthDay: 1973-09-28) age: 47
before4 <<< person: (name: Aki, gender: FEMALE, birthDay: 1973-11-14) age: 46
before4 <<< minAge: 38 maxAge: 60

after4 <<< p: (name: Money Penny, gender: FEMALE, birthDay: 1980-05-22) age: 40
after4 <<< p: (name: James Bond, gender: MALE, birthDay: 1970-04-03) age: 50
after4 <<< p: (name: Dink, gender: FEMALE, birthDay: 1965-01-31) age: 55
after4 <<< p: (name: Tatiana Romanova, gender: FEMALE, birthDay: 1982-08-19) age: 38
after4 <<< p: (name: Auric Goldfinger, gender: MALE, birthDay: 1960-08-15) age: 60
after4 <<< p: (name: Honey Rider, gender: FEMALE, birthDay: 1975-02-13) age: 45
after4 <<< p: (name: Julius No, gender: MALE, birthDay: 1965-07-04) age: 55
after4 <<< p: (name: Anya Amasova, gender: FEMALE, birthDay: 1972-06-17) age: 48
after4 <<< p: (name: Domino Derval, gender: FEMALE, birthDay: 1973-09-28) age: 47
after4 <<< p: (name: Aki, gender: FEMALE, birthDay: 1973-11-14) age: 46

before5 <<< did NOT find person with name ==>James<==
after5 <<< name ==>James<== noneMatch: true

before6 <<< person: (name: Tatiana Romanova, gender: FEMALE, birthDay: 1982-08-19)
after6 <<< p: (name: Tatiana Romanova, gender: FEMALE, birthDay: 1982-08-19)

before7 <<< person: (name: Auric Goldfinger, gender: MALE, birthDay: 1960-08-15)
after7 <<< p: (name: Auric Goldfinger, gender: MALE, birthDay: 1960-08-15)

before8:
MALE
(name: James Bond, gender: MALE, birthDay: 1970-04-03)
(name: Auric Goldfinger, gender: MALE, birthDay: 1960-08-15)
(name: Julius No, gender: MALE, birthDay: 1965-07-04)
FEMALE
(name: Money Penny, gender: FEMALE, birthDay: 1980-05-22)
(name: Dink, gender: FEMALE, birthDay: 1965-01-31)
(name: Tatiana Romanova, gender: FEMALE, birthDay: 1982-08-19)
(name: Honey Rider, gender: FEMALE, birthDay: 1975-02-13)
(name: Anya Amasova, gender: FEMALE, birthDay: 1972-06-17)
(name: Domino Derval, gender: FEMALE, birthDay: 1973-09-28)
(name: Aki, gender: FEMALE, birthDay: 1973-11-14)

after8:
MALE
(name: James Bond, gender: MALE, birthDay: 1970-04-03)
(name: Auric Goldfinger, gender: MALE, birthDay: 1960-08-15)
(name: Julius No, gender: MALE, birthDay: 1965-07-04)
FEMALE
(name: Money Penny, gender: FEMALE, birthDay: 1980-05-22)
(name: Dink, gender: FEMALE, birthDay: 1965-01-31)
(name: Tatiana Romanova, gender: FEMALE, birthDay: 1982-08-19)
(name: Honey Rider, gender: FEMALE, birthDay: 1975-02-13)
(name: Anya Amasova, gender: FEMALE, birthDay: 1972-06-17)
(name: Domino Derval, gender: FEMALE, birthDay: 1973-09-28)
(name: Aki, gender: FEMALE, birthDay: 1973-11-14)

The first set of line display the contents of a list of characters from the James Bond books and movies. As you might already know, the first actor who played the 007 agent, Sean Connery passed away recently. I am a James Bond fan. Have watched all the 25 movies multiple times and owned DVD copies of them. Some movies are better than others, but in my opinion, Sean Connery was the best actor for the role. He should have retired somewhat earlier.  In the last two movies he was casted into the roll he was not able to keep up with the action scenes. Such is life.

Please note that the date of birth of the movie characters, were made up by me. I just wanted to have some dates so we could process them.

Following the list which is in no specific order, we see pairs of function output named before# and after#. The “before” indicates that I tried not using a Java 8 feature, while the “after” indicates that I tries using Java 8 features. I should have given the functions better names, but I was just experimenting and ran out of time. In production one should always have names that represent what the function / method does.

    /**
     * Test scaffolding.
     * The date of birth for the characters was made up.
     */
    public static void main(String[] args) {

        // **** ****
        int age = 0;

        // **** create list of people ****
        List<Person> people = new ArrayList<Person>();

        // **** populate the list of people ****
        people.add(new Person("Money Penny", Gender.FEMALE, LocalDate.of(1980, Month.MAY, 22)));
        people.add(new Person("James Bond", Gender.MALE, LocalDate.of(1970, Month.APRIL, 3)));
        people.add(new Person("Dink", Gender.FEMALE, LocalDate.of(1965, Month.JANUARY, 31)));
        people.add(new Person("Tatiana Romanova", Gender.FEMALE, LocalDate.of(1982, Month.AUGUST, 19)));

        people.add(new Person("Auric Goldfinger", Gender.MALE, LocalDate.of(1960, Month.AUGUST, 15)));
        people.add(new Person("Honey Rider", Gender.FEMALE, LocalDate.of(1975, Month.FEBRUARY, 13)));
        people.add(new Person("Julius No", Gender.MALE, LocalDate.of(1965, Month.JULY, 4)));
        people.add(new Person("Anya Amasova", Gender.FEMALE, LocalDate.of(1972, Month.JUNE, 17)));

        people.add(new Person("Domino Derval", Gender.FEMALE, LocalDate.of(1973, Month.SEPTEMBER, 28)));
        people.add(new Person("Aki", Gender.FEMALE, LocalDate.of(1973, Month.NOVEMBER, 14)));


        // **** display the list of people ****
        people.forEach( p -> System.out.println("main <<< p: " + p) );
        System.out.println();


        // **** generate list of females ****
        before0(people);

        // **** generate list of females ****
        after0(people);


        // **** sort people ****
        before1(people);

        // **** sort people ****
        after1(people);


        // **** specify age ****
        age = 37;

        // **** all people are atleast this age ****
        before2(people, age);

        // **** all people are atleast this age ****
        after2(people, age);


        // **** specify age ****
        age = 61;

        // **** any person older than this age ****
        before3(people, age);

        // **** any person older than this age ****
        after3(people, age);


        // **** display people and age ****
        before4(people);

        // **** display people and age ****
        after4(people);


        // **** specify name ****
        String name = "James";

        // **** find any person with the specified name ****
        before5(people, name);

        // **** find any person with the specified ****
        after5(people, name);


        // **** oldest person ****
        before6(people);

        // **** oldest person ****
        after6(people);


        // **** youngest person ****
        before7(people);

        // **** youngest person ****
        after7(people);

        
        // **** group by gender ****
        before8(people);

        // **** group by gender ****
        after8(people);
    }

The test scaffolding is quite simple. We first create a List and populate it with some random James Bond characters. Note that age is not part of the records; their fake birthdates is. The reason for this is that if you use age, it will constantly change. If you use the birthdates, you can always compute the correct age. Most important, we want to experiment with the LocalDate class which was introduced in Java 8.

We then have a list of pairs of calls. The results should match. The first was written without Java 8 features while the second was written using Java 8. If I made a mistake by excluding or including something that was not available in the proper timeline, please leave me a note and I will correct the issue ASAP.

/**
 * Gender for characters.
 */
enum Gender {
    FEMALE, MALE, OTHER
}

The enumeration is used to assign gender to the characters. The first two are used. The last one is not. Once again, I made the decision on the gender. It does not reflect any thing about the character or the actors who played the role.

/**
 * Used to represent characters.
 */
class Person {

    // **** members ****
    private String name;
    private Gender gender;
    private LocalDate birthDay;
    private double weight;
    private double height;

    // **** constructors(s) ****
    public Person() {
    }

    public Person(String name, Gender gender, LocalDate birthDay) {
        this.name = name;
        this.gender = gender;
        this.birthDay = birthDay;
    }

    // **** getters ****
    public String getName() {
        return this.name;
    }

    public Gender getGender() {
        return this.gender;
    }

    public LocalDate getBirthDay() {
        return this.birthDay;
    }

    public double getWeight() {
        return this.weight;
    }

    public double getHeight() {
        return this.height;
    }

    // **** setters ****
    public void setName(String name) {
        this.name = name;
    }

    // **** ****
    @Override
    public String toString() {
        return "(name: " + this.name + ", gender: " + this.gender + ", birthDay: " + this.birthDay + ")";
    }
}

The Person class represents the actors. The weight and height fields are not used at all. If this were production code, I would have removed them from the class.

The constructors follow. Not sure if I use both. The first one was used for sure while I started to write the code. Not sure if it is used in the current implementation. If this were production code and if the first constructor is not used, I would have removed it.

The getter and setters for the used fields follow.

The final method is used to display the Person when needed. As you can see by the screen capture of the test scaffolding, this method is used many times.


/**
 * Used to compare the names of two characters.
 */
class PersonComparator implements Comparator&lt;Person&gt; {

    @Override
    public int compare(Person p1, Person p2) {

        // **** for comparison ****
        int nameCompare = p1.getName().compareTo(p2.getName());

        // **** ****
        return nameCompare;
    }
}

This is a comparator for the Person class. At some point we want to sort the records in the people list. We used this comparator.

    /**
     * Generate female list.
     * Imperative approach.
     */
    static void before0(List&lt;Person&gt; people) {

        // // **** display people list ****
        // for (Person person : people) {
        //     System.out.println("before0 &lt;&lt;&lt; person: " + person);
        // }
        // System.out.println();

        // **** generate female list ****
        List&lt;Person&gt; females = new ArrayList&lt;Person&gt;();
        for (int i = 0; i &lt; people.size(); i++) {
            if (people.get(i).getGender().equals(Gender.FEMALE))
                females.add(people.get(i));
        }

        // **** display female list ****
        for (Person f : females) {
            System.out.println("before0 &lt;&lt;&lt; f: " + f);
        }
        System.out.println();
    }

This and all the functions will have the people argument which contains a list of a few characters (I believe they are ten).

I left commented out code to display the list of people. Initially I displayed the list here for the first time. As you can see, the current implementation displays the list in the main function of the test scaffolding.

We create a list to hold the females (that does not sound nice). We then loop one person at a time. On each pass we check if the gender is FEMALE. If so we add the person to the female list.

When done we display the list of females.  For separation purpose, we follow the display of the list with a blank line.

    /**
     * Generate female list.
     */
    static void after0(List&lt;Person&gt; people) {

        // **** generate list of females ***
        List&lt;Person&gt; females = people.stream()
            .filter(person -&gt; person.getGender().equals(Gender.FEMALE))
            .collect(Collectors.toList());

        // **** display list of females ****
        // females.forEach(System.out::println);
        females.forEach( f -&gt; System.out.println("after0 &lt;&lt;&lt; f: " + f) );

        // **** for the looks ****
        System.out.println();
    }

This is the equivalent of the previous function. We generate the list of females using a stream. We then display the list of females. One of the equivalent statements is commented out. We follow the display by a blank line.

    /**
     * Sort the people list.
     * You can easily reverse the sorting order.
     */
    static void before1(List&lt;Person&gt; people) {

        // **** make a copy of the people list ****
        List&lt;Person&gt; sorted = new ArrayList&lt;Person&gt;();
        for (int i = 0; i &lt; people.size(); i++) {
            sorted.add(people.get(i));
        }

        // **** sort the list ****
        Collections.sort(sorted, new PersonComparator());

        // **** display the sorted list ****
        for (int i = 0; i &lt; people.size(); i++) {
            System.out.println("before1 &lt;&lt;&lt; person: " + sorted.get(i));
        }

        // **** for the looks ****
        System.out.println();
    }

We need to generate and display a sorted list of people. We start by declaring an array list. We populate the sorted array list which at this time is in the original order.

We then sort the list. Note that we saw the definition for the comparator in an earlier code snippet.

We display the sorted list and finish the function by displaying a new line.

   /**
     * Sort people list.
     * You can easily reverse the sorting order.
     */
    static void after1(List&lt;Person&gt; people) {

        // **** sort people list ****
        List&lt;Person&gt; sorted = people.stream()

            // .sorted(Comparator.comparing(Person::getName))
            // .sorted(Comparator.comparing(Person::getName).reversed())

            .sorted(Comparator.comparing(Person::getName).thenComparing(Person::getGender))

            .collect(Collectors.toList());

        // **** display sorted list ****
        sorted.forEach( p -&gt; System.out.println("after1 &lt;&lt;&lt; p: " + p) );

        // **** for the looks ****
        System.out.println();
    }

This is the equivalent of the last function. We use a stream to sort and collect the results into a list. The sorted list is displayed followed by an empty line.

I have commented out the lines that just sort by name in ascending and the second by descending order. Note that the current implementation sorts first by name and then by gender.

    /**
     * Check if all match condition.
     * This is not completely correct because Period uses LocalDate (Java 8).
     * Imperative approach.
     */
    static void before2(List&lt;Person&gt; people, int age) {

        // **** initialization ****
        boolean allMatch = true;

        // **** loop checking ages ****
        for (int i = 0; i &lt; people.size() &amp;&amp; allMatch == true; i++) {
            if (Period.between(people.get(i).getBirthDay(), LocalDate.now()).getYears() &lt;= age)
                allMatch = false;
        }

        // **** display result ****
        System.out.println("before2 &lt;&lt;&lt; age: " + age + " allMatch: " + allMatch);
    }

This function checks if all the people in the list have ages greater than the one specified as an argument. We start by defining and initializing a variable to true. We then loop through all the records computing the age of the person. If the age of a person is less than or equal to the specified age, the function displays false.

The result is displayed and a blank line is added.

    /**
     * Check if all match condition.
     */
    static void after2(List&lt;Person&gt; people, int age) {

        // **** check ages ****
        boolean allMatch = people.stream()
            .allMatch( p -&gt; Period.between(p.getBirthDay(), LocalDate.now()).getYears() &gt; age );

        // **** display results ****
        System.out.println("after2 &lt;&lt;&lt;  age: " + age + " allMatch: " + allMatch + "\n");
    }

This function is similar to the previous one. In this one we use a stream. The result is then displayed followed by a blank line..

    /**
     * Check if at least one person matches the condition.
     * Using an iterator; could have used a for loop.
     * This is not fair because Period uses LocalDate (Java 8).
     * Imperative approach.
     */
    static void before3(List&lt;Person&gt; people, int age) {

        // **** initialization ****
        boolean anyMatch = false;

        // **** traverse the list ****
        Iterator&lt;Person&gt; it = people.iterator();
        while (it.hasNext()) {

            // **** get next person ****
            Person person = it.next();

            // **** check if at this age or older ****
            if (Period.between(person.getBirthDay(), LocalDate.now()).getYears() &gt;= age) {
                anyMatch = true;
                break;
            }
        }

        // **** display result ****
        System.out.println("before3 &lt;&lt;&lt; age: " + age + " anyMatch: " + anyMatch);
    }

In this case we need to determine if at least one person is older than the specified age. We initialize a variable to false. We traverse the list of people using an iterator. We could just have used a ‘for’ loop as we have shown in previous functions. I just felt like using a different way to loop through the list.

We loop though the people list computing the age and determining if it is greater than the specified value. If so, we set the flag to true and exit the loop. We then display the result.

    /**
     * Check if at least one person matches the condition.
     */
    static void after3(List&lt;Person&gt; people, int age) {

        // **** ****
        boolean anyMatch = people.stream()
            .anyMatch( p -&gt; Period.between(p.getBirthDay(), LocalDate.now()).getYears() &gt;= age);

        // **** display result ****
        System.out.println("after3 &lt;&lt;&lt;  age: " + age + " anyMatch: " + anyMatch + "\n");           
    }

This is similar to the previous function. In this case we use a stream. When done we display the results followed by a blank line.

As you can see, the implementations with streams tend to be simpler to understand and take less space to write.

    /**
     * Display people and associated age.
     * Compute and display min and max ages. 
     * This is not fair because Period uses LocalDate (Java 8).
     * Imperative approach.
     */
    static void before4(List&lt;Person&gt; people) {

        // **** *****
        int minAge = Integer.MAX_VALUE;
        int maxAge = Integer.MIN_VALUE;

        // **** traverse list of people ****
        for (int i = 0; i &lt; people.size(); i++) {

            // **** compute age ****
            int age = Period.between(people.get(i).getBirthDay(), LocalDate.now()).getYears();

            // **** display person and age ****
            System.out.println("before4 &lt;&lt;&lt; person: " + people.get(i) + " age: " + age);

            // **** update min and max ages ****
            minAge = Math.min(minAge, age);
            maxAge = Math.max(maxAge, age);
        }

        // **** display min and max ages ****
        System.out.println("before4 &lt;&lt;&lt; minAge: " + minAge + " maxAge: " + maxAge + "\n");
    }

In this example we wish to display the record followed by the age of the person.

We initialize a couple variables that have nothing to do with computing the age. We just decided to get the minimum and maximum ages. I felt compelled to have such information as I was experimenting with the different methods provided by the streams API.

We then traverse the list of people. We compute the age and display the person and age.

We update the min and max age variables.

When done we display the min and max age variables.

   /**
     * Display people and associated age.
     * Compute and display min and max ages.
     */
    static void after4(List&lt;Person&gt; people) {

        // **** ****
        people.forEach( p -&gt; { 
            int age = Period.between(p.getBirthDay(), LocalDate.now()).getYears();
            System.out.println("after4 &lt;&lt;&lt; p: " + p + " age: " + age);
        });
    
        // **** for the looks ****
        System.out.println();
    }

In this example we use the ‘forEach’ method in the list class. We use a lambda expression to compute the age of the person and then display the person followed by the age.

When done we display a blank line.

Note than lambda functions are simple and easy to write. You need to be careful when using them. If you have a function or method and it is called in different places on your project, then you can easily locate the instances. Locating lambda functions is not as easy because they are anonymous. Just keep that in mind when developing code. The fact that a tool is available is not enough to use it when there are other tools that fit better the situation.

    /**
     * Find any person with the specified name.
     * Imperative approach.
     */
    static void before5(List&lt;Person&gt; people, String name) {

        // **** initialization ****
        Person person = null;
        boolean found = false;

        // **** ****
        for (int i = 0; i &lt; people.size() &amp;&amp; !found; i++) {

            // **** get current person ****
            person = people.get(i);

            // **** check name ****
            if (person.getName().equals(name)) {
                found = true;
            }
        }

        // **** display person with specified name (if any) ****
        if (found)
            System.out.println("before5 &lt;&lt;&lt; found person: " + person.toString());
        else
            System.out.println("before5 &lt;&lt;&lt; did NOT find person with name ==&gt;" + name + "&lt;==");
    }

We need to locate the name of a person in the people list. We start by defining two variables. We then loop though the list of people getting the current person on each pass. If the name matches, we flag it.

When done with the loop we display if the person with the specified name was found or not. If found we also display the complete person record.

    /**
     * Find any person with the specified name.
     */
    static void after5(List&lt;Person&gt; people, String name) {

        // **** ****
        boolean noneMatch = people.stream()
            .noneMatch( p -&gt; p.getName().equals(name));

        // **** display results ****
        System.out.println("after5 &lt;&lt;&lt; name ==&gt;" + name + "&lt;== noneMatch: " + noneMatch + "\n");
    }

In this function we also need to find the person with the specified name. We use the stream API. When done we display the name and the Boolean condition.

    /**
     * Youngest person?
     * Imperative approach.
     */
    static void before6(List&lt;Person&gt; people) {

        // **** initialization*****
        int minAge = Integer.MAX_VALUE;
        int minIndex = -1;

        // **** traverse list of people ****
        for (int i = 0; i &lt; people.size(); i++) {

            // **** compute age ****
            int age = Period.between(people.get(i).getBirthDay(), LocalDate.now()).getYears();

            // **** update variables (if needed) ****
            if (age &lt; minAge) {
                minAge = age;
                minIndex = i;
            }
        }

        // **** display the youngest person ****
        System.out.println("before6 &lt;&lt;&lt; person: " + people.get(minIndex));
    }

We need to find the youngest person in the list. We initialize a variable with the minimum age and the index in the list.

 

We traverse the list. For each record we compute the age. If needed, we update the variables. When done we display the record for the youngest person.

    /**
     * Youngest person?
     */
    static void after6(List&lt;Person&gt; people) {

        people.stream()
            .max(Comparator.comparing(Person::getBirthDay))
            .ifPresent( p -&gt; System.out.println("after6 &lt;&lt;&lt; p: " + p + "\n"));
    }

This is the equivalent function using the streams API. We achieve the same results with fewer lines of code. Note that

    /**
     * Oldest person?
     * Imperative approach.
     */
    static void before7(List&lt;Person&gt; people) {

        // **** initialization*****
        int maxAge = Integer.MIN_VALUE;
        int maxIndex = -1;

        // **** traverse list of people ****
        for (int i = 0; i &lt; people.size(); i++) {

            // **** compute age ****
            int age = Period.between(people.get(i).getBirthDay(), LocalDate.now()).getYears();

            // **** update variables (if needed) ****
            if (age &gt; maxAge) {
                maxAge = age;
                maxIndex = i;
            }
        }

        // **** display the oldest person ****
        System.out.println("before7 &lt;&lt;&lt; person: " + people.get(maxIndex));
    }

In this case we are looking for the oldest person. We initialize a couple variables. We traverse the list of people. We compute the age. If the person is older than the maximum age, we update the auxiliary variable.

When done we display the results.

    /**
     * Oldest person?
     */
    static void after7(List&lt;Person&gt; people) {

        people.stream()
            .min(Comparator.comparing(Person::getBirthDay))
            .ifPresent( p -&gt; System.out.println("after7 &lt;&lt;&lt; p: " + p + "\n"));
    }

This is similar to the last function. In this case we use the stream API. We compare the birthdates and display the results.


    /**
     * Group people by gender.
     * Imperative approach.
     */
    static void before8(List&lt;Person&gt; people) {

        // **** hash map ****
        Map&lt;Gender, List&lt;Person&gt;&gt; mapByGender = new HashMap&lt;Gender, List&lt;Person&gt;&gt;();

        // **** generate hash map by gender ****
        for (int i = 0; i &lt; people.size(); i++) {

            // **** get this person ****
            Person person = people.get(i);

            // **** ****
            if (!mapByGender.containsKey(person.getGender())) {
                List&lt;Person&gt; people1 = new ArrayList&lt;Person&gt;();
                people1.add(person);
                mapByGender.put(person.getGender(), people1);
            } else {
                List&lt;Person&gt; people1 = mapByGender.get(person.getGender());
                people1.add(person);
            }
        }

        // **** entry set of hash map ****
        Set&lt;Entry&lt;Gender, List&lt;Person&gt;&gt;&gt; set = mapByGender.entrySet();

        // **** for the looks ****
        System.out.println("before8:");

        // **** iterate through the genders ****
        Iterator&lt;Entry&lt;Gender, List&lt;Person&gt;&gt;&gt; it = set.iterator();
        while (it.hasNext()) {

            // **** ****
            Entry&lt;Gender, List&lt;Person&gt;&gt; e = it.next();

            // **** ****
            System.out.println(e.getKey());

            // **** get the list ****
            List&lt;Person&gt; l = e.getValue();

            // **** iterate through each person ****
            for (int i = 0; i &lt; l.size(); i++) {
                System.out.println(l.get(i));
            }
        }

        // **** for the looks ****
        System.out.println();
    }

We need to group people by gender.

We declare a hash map to hold the list of people for a given gender. We start by populating the hash map. We do so by traversing the list of people. For each person we insert the record in the associated list. Note that when we encounter the first record for each gender we need to create the list associated with such gender.

We extract the hash map contents into a set of entries.

We declare an iterator and iterate the set. We get an entry. We display the key which maps to the gender. Following that we iterate through the list displaying the people with the current gender.

When done we display a blank line.

    /**
     * Group people by gender.
     */
    static void after8(List&lt;Person&gt; people) {

        // **** ****
        Map&lt;Gender, List&lt;Person&gt;&gt; mapByGender = people.stream()
            .collect(Collectors.groupingBy(Person::getGender));

        // **** ****
        System.out.println("after8:");
        mapByGender.forEach( (gender, people1) -&gt; {
            System.out.println(gender);
            people1.forEach(System.out::println);
        });

        // **** for the looks ****
        System.out.println();
    }

This is similar to the previous function. In this case we use the stream API. We generate a map based on gender. We then display the associated values in the list.

We display a blank line.

That is all folks. There are many other methods in the stream API. It is a good idea to check if there is a method in the API that would help simplify and understand an operation.

Hope you enjoyed solving this problem as much as I did. The entire code for this project can be found in my GitHub repository.

If you have comments or questions regarding this, or any other post in this blog, or if you would like for me to help out with any phase in the SDLC (Software Development Life Cycle) of a project associated with a product or service, please do not hesitate and leave me a note below. If you prefer, send me a private e-mail message. I will reply as soon as possible.

Keep on reading and experimenting. It is the best way to learn, become proficient, refresh your knowledge and enhance your developer toolset!

One last thing, many thanks to all 3,782 subscribers to this blog!!!

Keep safe during the COVID-19 pandemic and help restart the world economy.

John

E-mail:  john.canessa@gmail.com

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.