【问题标题】:Java Stream sortingBy and filter()Java StreamsortingBy 和 filter()
【发布时间】:2020-04-22 18:44:43
【问题描述】:

我有一个关于 Java 8 流的具体问题。即使听起来不太复杂,我也找不到任何接近解决问题的方法。

我想将List<Person> 转换为Map<Company, List<Person>>。该公司是一个枚举。现在Map 应该由Company 分组(到目前为止我一切正常。在我想通过属性过滤Map 的所有List<Person> 之后:Person 的名称。我只想要将名字最长的人放在里面。例如:

这是我目前得到的:

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;

public class PersonMap {

    public static void main(String[] args) {
        Person person1 = new Person("Pete", Company.APPLE);
        Person person2 = new Person("Joe", Company.APPLE);
        Person person3 = new Person("Jack", Company.APPLE);
        Person person4 = new Person("Robert", Company.MICROSOFT);
        Person person5 = new Person("Cate", Company.MICROSOFT);
        Person person6 = new Person("Abi", Company.MICROSOFT);

        List<Person> list = new LinkedList<>();
        list.add(person1);
        list.add(person2);
        list.add(person3);
        list.add(person4);
        list.add(person5);
        list.add(person6);
    }

    public Map<Company, List<Person>> groupPersonByCompanyAndFilterByName (List <Person> list){
        Map<Company, List<Person>> outputMap =
                list.stream().collect(Collectors.groupingBy(Person::getCompany))
                .entrySet().stream()
                .filter(p -> p.getValue().stream().toString().length() !=
                p.getValue().stream().map(Person::getName).mapToInt(String::length).max().getAsInt())
                .collect(Collectors.toMap(Entry::getKey, Entry::getValue));

        return outputMap;
    }
}

过滤功能在这里不起作用。我想要的输出是:

{MICROSOFT=["person4"], APPLE=[person1, person3]}

谁有好的知识可以帮帮我!

【问题讨论】:

  • 我不认为只用流来做到这一点的好方法。您可以在之后浏览每个单独的子列表,找到并记住最长名称的长度,然后在第二遍中过滤掉没有该长度的所有内容。或者,您浏览子列表一次并将所有名称添加到另一个列表中,如果它们等于您迄今为止遇到的最长的名称。如果您遇到的名称比您目前找到的最长名称要长,请先清除当前列表,然后再添加该名称。

标签: java dictionary filter java-stream groupingby


【解决方案1】:

这是一种使用 teeing 的方法

public Map<Company, List<Person>> groupPersonByCompanyAndFilterByName (List <Person> list){
    return list.stream().collect(groupingBy(Person::getCompany,
           teeing(
                  mapping(Person::getName, maxBy(Comparator.comparing(String::length))),
                  toList(),
                  (a, b) -> b.stream().filter(x -> x.getName().length() == a.get().length())
                                      .collect(toList())

           )
    ));

}

【讨论】:

  • 不错!但它确实需要 Java 12+
【解决方案2】:

为了获得更好的性能,您不应在单个流链中执行此操作。

试试这个:

List<Person> list = new LinkedList<>();
list.add(new Person("Pete", Company.APPLE));
list.add(new Person("Joe", Company.APPLE));
list.add(new Person("Jack", Company.APPLE));
list.add(new Person("Robert", Company.MICROSOFT));
list.add(new Person("Cate", Company.MICROSOFT));
list.add(new Person("Abi", Company.MICROSOFT));

Map<Company, List<Person>> map = list.stream().collect(Collectors.groupingBy(Person::getCompany));
map.values().forEach(persons -> {
    int maxLen = persons.stream().mapToInt(p -> p.getName().length()).max().getAsInt();
    persons.removeIf(p -> p.getName().length() != maxLen);
});

System.out.println(map);

输出

{APPLE=[Pete, Jack], MICROSOFT=[Robert]}

对于上面的代码,我使用了以下额外的类:

enum Company {
    APPLE, MICROSOFT
}

class Person {
    private final String name;
    private final Company company;
    public Person(String name, Company company) {
        this.name = name;
        this.company = company;
    }
    public String getName() {
        return this.name;
    }
    public Company getCompany() {
        return this.company;
    }
    @Override
    public String toString() {
        return this.name;
    }
}

【讨论】:

    【解决方案3】:

    您可以按多个字段Companyname 的长度进行分组

    Map<Company, Map<Integer, List<Person>>> map = list.stream()
                .collect(Collectors.groupingBy(Person::getCompany, Collectors.groupingBy(p -> p.getName().length())));
    

    并且从外部映射key 和内部映射value 创建Entries 具有最大长度(名称),即内部映射key

    Map<Company, List<Person>> resultMap = map.entrySet().stream()
                .map(e -> new AbstractMap.SimpleEntry<>(e.getKey(),
                        e.getValue().entrySet().stream()
                                .max(Comparator.comparing(Map.Entry<Integer, List<Person>>::getKey)).get().getValue()))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
    

    您也可以将这两个操作组合成一个流

    【讨论】:

      【解决方案4】:
           Person person1 = new Person("Pete", "APPLE");
                  Person person2 = new Person("Joe", "APPLE");
                  Person person3 = new Person("Jack", "APPLE");
                  Person person4 = new Person("Robert", "MICROSOFT");
                  Person person5 = new Person("Cate", "MICROSOFT");
                  Person person6 = new Person("Abi", "MICROSOFT");
      
                  List<Person> list = new ArrayList<>();
      
                  list.add(person1);
                  list.add(person2);
                  list.add(person3);
                  list.add(person4);
                  list.add(person5);
                  list.add(person6);
      
        //Adapt your structure and with this function you can group  
                  Map<String, List<Person>> map = list.stream().collect(Collectors.groupingBy(person -> person.getCompany()));
      
                  for (String name: map.keySet()){
                      List<Person> persons = map.get(name);
                      System.out.println("[" + name + "]");
                      persons.forEach(s -> System.out.println(s.getName()));
                  }
      

      输出日志: [苹果] 皮特 乔 杰克 [微软] 罗伯特 凯特 阿比

      【讨论】:

      • 我刚刚写给你如何更好地分组。另一方面,我不明白你想要实现什么
      • OP 想要按每个公司名称最长的人进行分组。如果最长的名字并列,请包括所有这些长度。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-09-04
      • 1970-01-01
      • 2014-01-04
      • 1970-01-01
      • 1970-01-01
      • 2015-08-24
      • 1970-01-01
      相关资源
      最近更新 更多