我有Person
课程:
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;
}
}
App
类:
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())));
}
}
在App
课程中,PeoplePerSkill
方法需要返回每个技能的Set
人名。这意味着许多人可以拥有一项技能。
我坚持使用groupingBy(p->p..........., )
我无法获得String
技能的名字,我尝试了很多方法,但事情变得陌生:(。
顺便说一句,目前我的代码返回Map<Object, Set<String>>
答案 0 :(得分:6)
你可以通过平面映射来做到这一点,虽然它可能看起来不太漂亮:
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
创建了一对(skill, person name)
对,其收集方式与您的非常相似。我正在使用AbstractMap.SimpleEntry
类来表示该对,您可以使用其他东西。
使用我的StreamEx库可以更好地解决此任务:
return StreamEx.of(this.people)
.mapToEntry(p -> p.getSkills().keySet(), Person::getName)
.flatMapKeys(Set::stream)
.grouping(toSet());
在内部它几乎相同,只是语法糖。
更新:似乎我的原始解决方案是错误的:它返回了地图person_name -> [skills]
,但如果我正确理解了OP,他想要地图skill -> [person_names]
。答案已编辑。
答案 1 :(得分:1)
我不确定溪流是否会让你的生活更轻松。 IMO这个代码更容易阅读和更清洁。
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;
}
你也可以简化&#34;
map.putIfAbsent(skill, new HashSet<>());
map.get(skill).add(person.getName());
带
map.computeIfAbsent(skill, k -> new HashSet<>()).add(person.getName());
答案 2 :(得分:1)
如果您可以在代码中使用外部库,则可能需要考虑使用Multimap
而不是Map<String, Set<String>>
。不幸的是,使用Multimap
的解决方案将需要更多的样板,因为JDK没有正式支持它,但它应该导致“更清洁”的解决方案:
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]}
请注意我必须如何向toMultimap()
提供通用参数。 Java 8具有更好的通用推理,但它does not infer chained method calls。
您需要显式提供泛型参数或声明局部变量Function<Map<String, Set<String>>, Multimap<String, String>> toMultimap
,以便编译器正确推断类型参数。