【问题标题】:Stuck with lambda expression and Map卡在 lambda 表达式和 Map
【发布时间】:2015-07-04 16:51:11
【问题描述】:

我有 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-&gt;p..........., ) 我只是无法获得技能名称的String,我尝试了很多方法但事情变得陌生:(。

顺便说一句,目前我的代码返回Map&lt;Object, Set&lt;String&gt;&gt;

【问题讨论】:

    标签: java lambda java-8 java-stream


    【解决方案1】:

    您可以通过平面映射来实现,尽管它可能看起来不太漂亮:

    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 -&gt; [skills],但如果我正确理解了OP,他想要地图skill -&gt; [person_names]。答案已编辑。

    【讨论】:

      【解决方案2】:

      我不确定流是否会让您在这里的生活更轻松。 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;
      }
      

      你也可以“简化”

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

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

      【讨论】:

        【解决方案3】:

        如果您可以在代码中使用外部库,您可能需要考虑使用Multimap 而不是Map&lt;String, Set&lt;String&gt;&gt;。不幸的是,使用 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&lt;Map&lt;String, Set&lt;String&gt;&gt;, Multimap&lt;String, String&gt;&gt; toMultimap,以便编译器正确推断类型参数。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-07-02
          • 1970-01-01
          • 2020-07-16
          • 1970-01-01
          • 2016-01-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多