简体   繁体   中英

How do you convert Map<String, Person> to Map<String, Integer> in java streams inside collect?

Imagine you have a list of people, Roberts, Pauls, Richards, etc, these are people grouped by name into Map<String, List<Person>> . You want to find the oldest Paul, Robert, etc... You can do it like so:

public static void main(String... args) {
        List<Person> people = Arrays.asList(
                new Person(23, "Paul"),
                new Person(24, "Robert"),
                new Person(32, "Paul"),
                new Person(10, "Robert"),
                new Person(4, "Richard"),
                new Person(60, "Richard"),
                new Person(9, "Robert"),
                new Person(26, "Robert")
        );

        Person dummy = new Person(0, "");
        var mapping = people.stream().collect(groupingBy(Person::getName, reducing(dummy, (p1, p2) -> p1.getAge() < p2.getAge() ? p2 : p1)));
        mapping.entrySet().forEach(System.out::println);
    }

Say, I want to get a mapping in the form of Map<String, Integer> instead of Map<String, Person> , I can do it like so:

var mapping = people.stream().collect(groupingBy(Person::getName, mapping(Person::getAge, reducing(0, (p1, p2) -> p1 < p2 ? p2 : p1))));

The steps above are:

  • Group by name into Map<String/*Name*/, List<Person>>
  • Map that List<Person> into List<Integer>
  • Find maximum Integer in those lists.

I was wondering how to do:

  • Group by name into Map<String, List<Person>>
  • Find the oldest person in each group name, getting Map<String, Person>
  • Convert Map<String, Person> into Map<String, Integer> . And I want to do all that inside that chain of groupingBy's, reducing's and mapping's.

This is the "pseudocode":

var mapping = people.stream().collect(groupingBy(Person::getName, reducing(dummy, (p1, p2) -> p1.getAge() < p2.getAge() ? p2 : p1 /*, have to write some other collector factory method here*/)));

How can I achieve this?

It is more straightforward to do this with the 3-argument version of toMap collector :

people.stream().collect(toMap(
        Person::getName, 
        Person::getAge, 
        Integer::max
    ));

If you really want to use groupingBy as mentioned in the problem statement, you can still do it like this:

final Map<String, Integer> oldestPersonByName = people.stream()
        .collect(Collectors.groupingBy(Person::getName, Collectors.reducing(0, Person::getAge, Integer::max)));

For the reduction you have to pass an identity element, mapping function and a binary operator used to reduce, which is max in this case.

I think it would be like the following, also using maxBy instead of reducing .

people.stream().collect(
    groupingBy(
        Person::getName,
        collectingAndThen(
            maxBy(Comparator.comparing(Person::getAge)),
            (Optional<Person> max) -> max.get().getAge()
        )
    )
);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM