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)
您可以链接分组收集器,这将为您提供多级地图。但是,如果您想按 2 个以上字段进行分组,则这并不理想。
更好的选择是重写类中的equals和hashcode方法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)
您可以通过使用以下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方法,其行为与我上面建议的语句相同。