Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stuck with java8 lambda expression

I have Map<Integer,Doctor> docLib=new HashMap<>(); to save class of Doctor.

Class Doctor has methods:getSpecialization() return a String,
getPatients() to return a collection of class Person.

In the main method, I type:

public Map<String,Set<Person>> getPatientsPerSpecialization(){
    Map<String,Set<Person>> res=this.docLib.entrySet().stream().
                         map(d->d.getValue()).
                         collect(groupingBy(d->d.getSpecialization(),
                                            d.getPatients()) //error
                                 );
   return res;
}

As you can see, I have problem with groupingBy,I try to send the same value d to the method, but it's wrong. How to solve this?

like image 531
Feng Avatar asked Jun 20 '15 05:06

Feng


2 Answers

You need a second Collector for that mapping :

public Map<String,Set<Person>> getPatientsPerSpecialization(){
    return this.docLib
               .values()
               .stream()
               .collect(Colectors.groupingBy(Doctor::getSpecialization,
                                             Collectors.mapping(Doctor::getPatients,toSet()))
                       );
}

EDIT:

I think my original answer may be wrong (it's hard to say without being able to test it). Since Doctor::getPatients returns a Collection, I think my code may return a Map<String,Set<Collection<Person>>> instead of the desired Map<String,Set<Person>>.

The easiest way to overcome that is to iterate over that Map again to produce the desired Map :

public Map<String,Set<Person>> getPatientsPerSpecialization(){
    return this.docLib
               .values()
               .stream()
               .collect(Colectors.groupingBy(Doctor::getSpecialization,
                                             Collectors.mapping(Doctor::getPatients,toSet()))
                       )
               .entrySet()
               .stream()
               .collect (Collectors.toMap (e -> e.getKey(),
                                           e -> e.getValue().stream().flatMap(c -> c.stream()).collect(Collectors.toSet()))
                        );
}

Perhaps there's a way to get the same result with a single Stream pipeline, but I can't see it right now.

like image 132
Eran Avatar answered Nov 04 '22 04:11

Eran


Instead of groupingBy, you could use toMap:

public Map<String, Set<Person>> getPatientsPerSpecialization() {
    return docLib.values()
                 .stream()
                 .collect(toMap(Doctor::getSpecialization,
                                d -> new HashSet<>(d.getPatients()),
                                (p1, p2) -> Stream.concat(p1.stream(), p2.stream()).collect(toSet())));
}

What it does is that it groups the doctors per specialization and map each one to a set of the patients it has (so a Map<String, Set<Person>>).

If, when collecting the data from the pipeline, you encounter a doctor with a specialization that is already stored as a key in the map, you use the merge function to produce a new set of values with both sets (the set that is already stored as a value for the key, and the set that you want to associate with the key).

like image 2
Alexis C. Avatar answered Nov 04 '22 04:11

Alexis C.