Functional Programming in Java, Second Edition: Nicer code pf page 7

I suggest this nicer code for the Statistics.java on page on page 70 (we are doing streams after).

Not using a main() but a JUnit 5 Test Case:

import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.averagingDouble;
import static java.util.stream.Collectors.summarizingDouble;

public class TransformingData {

    record Pair(String str, Number num) {

        public String toString() {
            return str + ": " + num;
        }
    }

    @Test
    public void statistics() {
        var statistics = Person.getPeople().stream()
                .collect(
                        summarizingDouble(person -> person.emailAddresses().size()));
        var pairs = new ArrayList<Pair>();
        pairs.add(new Pair("Number of people", statistics.getCount()));
        pairs.add(new Pair("Number of email addresses", statistics.getSum()));
        pairs.add(new Pair("Average number of email addresses", statistics.getAverage()));
        pairs.add(new Pair("Max number of email addresses", statistics.getMax()));
        pairs.add(new Pair("Min number of email addresses", statistics.getMin()));
        String res = pairs.stream().map(Pair::toString).collect(Collectors.joining("\n"));
        System.out.println(res);
    }
}

Similary for the next exercises

    record PairB(String str, Boolean b) {

        public String toString() {
            return str + ": " + b;
        }
    }

    public void printPairsB(List<PairB> pairs) {
        var res = pairs.stream().map(PairB::toString).collect(Collectors.joining("\n"));
        System.out.println(res);
    }

    @Test
    public void checkingForCriteriaAny() {
        List<Person> people = Person.getPeople();
        var pairs = new ArrayList<PairB>();
        pairs.add(new PairB(
                "Anyone has email address: ",
                people.stream().anyMatch(person -> person.emailAddresses().size() > 0)));
        pairs.add(new PairB(
                "Anyone has more than 10 email address: ",
                people.stream().anyMatch(person -> person.emailAddresses().size() > 10)));
        printPairsB(pairs);
    }

    @Test
    public void checkingForCriteriaAll() {
        List<Person> people = Person.getPeople();
        var pairs = new ArrayList<PairB>();
        pairs.add(new PairB(
                "Everyone has at least one email address: ",
                people.stream().allMatch(person -> person.emailAddresses().size() > 0)));
        pairs.add(new PairB(
                "Everyone has zero or more email address: ",
                people.stream().allMatch(person -> person.emailAddresses().size() >= 0)));
        printPairsB(pairs);
    }

And even the next

    record PairN(String str, Number num) {

        public String toString() {
            return str + ": " + num;
        }
    }

    public void printPairsN(List<PairN> pairs) {
        var res = pairs.stream().map(PairN::toString).collect(Collectors.joining("\n"));
        System.out.println(res);
    }

    @Test
    public void partitioning() {
        List<Person> people = Person.getPeople();
        Map<Boolean, List<Person>> partitions =
                people.stream()
                        .collect(partitioningBy(person -> person.emailAddresses().size() > 1));
        var pairs = new ArrayList<PairN>();
        pairs.add(new PairN("Number of people with at most one email address", partitions.get(false).size()));
        pairs.add(new PairN("Number of people with multiple email addresses" , partitions.get(true).size()));
        printPairsN(pairs);
    }