2015-07-04 20 views
7

ile sıkışmış:Ben <code>Person</code> sınıf var lambda ifade ve Harita

import java.util.*; 
public class Person { 
    private String name; 
    Map<String,Integer> Skills=new HashMap<>(); // skill name(String) and level(int) 

    public String getName(){ 
     return this.name; 
    } 
    public Map<String,Integer> getSkills(){ 
     return this.Skills; 
    } 
} 

Ve App sınıfı: App sınıfta

import java.util.*; 
import java.util.Map.Entry; 
import static java.util.stream.Collectors.*; 
import static java.util.Comparator.*; 
public class App { 
    private List<Person> people=new ArrayList<>(); // the people in the company 

    public Map<String,Set<String>> PeoplePerSkill(){ 
     return this.people.stream().collect(groupingBy(p-> p.getSkills().keySet() //<-get 
                      //^problem here 
            ,mapping(Person::getName,toSet()))); 
    } 
} 

PeoplePerSkill yöntem insanların isimleri Set başına dönmek gerekiyor beceri. Bu, bir kişinin birçok kişiye ait olabileceği anlamına gelir.

Ben şeyler yolu yabancı olsun :(.

arada, şu anda benim kod Map<Object, Set<String>>

cevap

6
döndürür birçok yolu denediklerini ancak, sadece yeteneğin adının String alamayan groupingBy(p->p...........,) ile sıkışmış İşte

public Map<String,Set<String>> PeoplePerSkill(){ 
    return this.people.stream() 
     .<Entry<String, String>>flatMap(p -> 
      p.getSkills().keySet() 
       .stream() 
       .map(s -> new AbstractMap.SimpleEntry<>(s, p.getName()))) 
     .collect(groupingBy(Entry::getKey, mapping(Entry::getValue, toSet()))); 
} 

flatMap col olan çiftleri (skill, person name) akışı oluşturur: muhtemelen çok güzel görünmüyor olsa

Sen, düz haritalama ile yapabilirsiniz seninkiyle oldukça benzer bir tarzda. Parçayı temsil etmek için AbstractMap.SimpleEntry sınıfını kullanıyorum, başka bir şey kullanabilirsiniz. Benim StreamEx kütüphane bu görevi kullanma

güzel çözülebilir:

return StreamEx.of(this.people) 
     .mapToEntry(p -> p.getSkills().keySet(), Person::getName) 
     .flatMapKeys(Set::stream) 
     .grouping(toSet()); 

Dahili olarak neredeyse aynı, sadece sözdizimsel şeker var.

Güncelleme: benim orijinal çözüm yanlış olduğunu görünüyor: o haritayı person_name -> [skills] döndü ama doğru OP anlıyorum, o haritayı skill -> [person_names] istiyor. Cevap düzenlendi.

1

Akışların burada hayatınızı kolaylaştıracağından emin değilim. IMO bu kod okumak ve temizlemek için çok daha kolay.

public Map<String, Set<String>> peoplePerSkill() { 

    Map<String, Set<String>> map = new HashMap<>(); 

    for (Person person : people) { 
     for (String skill : person.getSkills().keySet()) { 
      map.putIfAbsent(skill, new HashSet<>()); 
      map.get(skill).add(person.getName()); 
     } 
    } 

    return map; 
} 

kodunuzda harici kütüphaneleri kullanabilirsek Ayrıca

map.putIfAbsent(skill, new HashSet<>()); 
map.get(skill).add(person.getName()); 

map.computeIfAbsent(skill, k -> new HashSet<>()).add(person.getName()); 
1

ile "basitleştirmek" olabilir, bir Map<String, Set<String>> yerine Multimap kullanarak düşünebilirsiniz . Ne yazık ki, bir Multimap kullanarak bir çözüm resmen JDK tarafından desteklenmemektedir beri daha klişe gerektiren gidiyor, ama bir "temizleyici" çözüm gitmelidir:

public static void main(String[] args) { 
    Person larry = new Person("larry"); 
    larry.getSkills().put("programming", 0); 
    larry.getSkills().put("cooking", 0); 

    Person nishka = new Person("nishka"); 
    nishka.getSkills().put("programming", 0); 
    nishka.getSkills().put("cooking", 0); 

    Person mitul = new Person("mitul"); 
    mitul.getSkills().put("running", 0); 
    mitul.getSkills().put("cooking", 0); 

    Person rebecca = new Person("rebecca"); 
    rebecca.getSkills().put("running", 0); 
    rebecca.getSkills().put("programming", 0); 

    List<Person> people = Arrays.asList(larry, nishka, mitul, rebecca); 

    Multimap<String, String> peopleBySkills = people.stream().collect(
     collectingAndThen(toMap(Person::getName, p -> p.getSkills().keySet()), 
      CollectingMultimap.<String, String, Set<String>> toMultimap() 
       .andThen(invert()))); 
    System.out.println(peopleBySkills); 
    } 

    private static <K, V, I extends Iterable<V>> Function<Map<K, I>, Multimap<K, V>> toMultimap() { 
    return m -> { 
     Multimap<K, V> map = ArrayListMultimap.create(); 
     m.entrySet().forEach(e -> map.putAll(e.getKey(), e.getValue())); 
     return map; 
    }; 
    } 

    private static <K, V> Function<Multimap<K, V>, Multimap<V, K>> invert() { 
    return m -> { 
     return Multimaps.invertFrom(m, ArrayListMultimap.create()); 
    }; 
    } 

{running=[mitul, rebecca], cooking=[nishka, larry, mitul], programming=[nishka, larry, rebecca]} 

Bildirimi nasıl Jenerik parametreleri toMultimap()'a vermek zorunda kaldım. Java 8'in çok daha iyi jenerik çıkarımı vardır, ancak does not infer chained method calls.

Derleyici için tür parametrelerini doğru şekilde tahmin edebilmesi için ya jenerik parametreleri açıkça belirtmeniz ya da yerel bir değişken Function<Map<String, Set<String>>, Multimap<String, String>> toMultimap bildirmeniz gerekir.

İlgili konular