Java 8并行流并发分组

use*_*053 6 java java-8 java-stream

假设我有一堂课

Class Person {
  String name;
  String uid;
  String phone;
}
Run Code Online (Sandbox Code Playgroud)

我正在尝试按班级的所有领域分组。我如何在JAVA 8中使用并行流来转换

List<Person> into Map<String,Set<Person>>
Run Code Online (Sandbox Code Playgroud)

映射的键是类中每个字段的值。JAVA 8以下示例将单个字段分组,如何将一个类的所有字段归为一个Map?

ConcurrentMap<Person.Sex, List<Person>> byGender =
roster
    .parallelStream()
    .collect(
        Collectors.groupingByConcurrent(Person::getGender));
Run Code Online (Sandbox Code Playgroud)

Ous*_* D. 5

您可以链接分组收集器,这将为您提供多级地图。但是,如果您想按 2 个以上字段进行分组,则这并不理想。

更好的选择是重写类中的equalshashcode方法Person来定义两个给定对象的相等性,在本例中这将是所有上述字段。Person然后你可以按ie分组,groupingByConcurrent(Function.identity())在这种情况下你最终会得到:

ConcurrentMap<Person, List<Person>> resultSet = ....
Run Code Online (Sandbox Code Playgroud)

例子:

class Person {
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Person person = (Person) o;

        if (name != null ? !name.equals(person.name) : person.name != null) return false;
        if (uid != null ? !uid.equals(person.uid) : person.uid != null) return false;
        return phone != null ? phone.equals(person.phone) : person.phone == null;
    }

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + (uid != null ? uid.hashCode() : 0);
        result = 31 * result + (phone != null ? phone.hashCode() : 0);
        return result;
    }

    private String name;
    private String uid; // these should be private, don't expose
    private String phone;

   // getters where necessary
   // setters where necessary
}
Run Code Online (Sandbox Code Playgroud)

然后:

ConcurrentMap<Person, List<Person>> resultSet = list.parallelStream()
                .collect(Collectors.groupingByConcurrent(Function.identity()));
Run Code Online (Sandbox Code Playgroud)


Lin*_*ica 5

您可以通过使用以下of静态工厂方法来做到这一点Collector

Map<String, Set<Person>> groupBy = persons.parallelStream()
    .collect(Collector.of(
        ConcurrentHashMap::new,
        ( map, person ) -> {
            map.computeIfAbsent(person.name, k -> new HashSet<>()).add(person);
            map.computeIfAbsent(person.uid, k -> new HashSet<>()).add(person);
            map.computeIfAbsent(person.phone, k -> new HashSet<>()).add(person);
        },
        ( a, b ) -> {
            b.forEach(( key, set ) -> a.computeIfAbsent(key, k -> new HashSet<>()).addAll(set));
            return a;
        }
    ));
Run Code Online (Sandbox Code Playgroud)

正如 Holger 在评论中建议的那样,以下方法可以优于上述方法:

Map<String, Set<Person>> groupBy = persons.parallelStream()
     .collect(HashMap::new, (m, p) -> { 
         m.computeIfAbsent(p.name, k -> new HashSet<>()).add(p); 
         m.computeIfAbsent(p.uid, k -> new HashSet<>()).add(p); 
         m.computeIfAbsent(p.phone, k -> new HashSet<>()).add(p); 
     }, (a, b) -> b.forEach((key, set) -> {
         a.computeIfAbsent(key, k -> new HashSet<>()).addAll(set));
     });
Run Code Online (Sandbox Code Playgroud)

它使用重载collect方法,其行为与我上面建议的语句相同。

  • 这里不需要使用ConcurrentHashMap,普通的HashMap就可以了。如果指定了“CONCURRENT”特征,则需要一个“ConcurrentHashMap”。顺便说一句,您可以使用“collect”的三参数版本进一步简化解决方案:“persons.parallelStream() .collect(HashMap::new, (m, p) -&gt; { m.computeIfAbsent(p .name, k -&gt; new HashSet&lt;&gt;()).add(p); m.computeIfAbsent(p.uid, k -&gt; new HashSet&lt;&gt;()).add(p); m.computeIfAbsent(p.phone , k -&gt; new HashSet&lt;&gt;()).add(p); }, (a, b) -&gt; b.forEach((key, set) -&gt; a.computeIfAbsent(key, k -&gt; new HashSet&lt;&gt; ()).addAll(set)));` (2认同)