Java 8:按字段名称对字段值进行分组

时间:2016-04-29 23:02:00

标签: java java-8 java-stream

我正在尝试找到一种更优雅的方法来创建一个地图,该地图使用Java 8按字段名称对字段值进行分组,而不是以下内容:

@Test
public void groupFieldValuesByFieldNames() {
    Person lawrence = aPerson().withFirstName("Lawrence").withLastName("Warren").born();
    Person gracie = aPerson().withFirstName("Gracie").withLastName("Ness").born();

    Map<String, List<String>> valuesByFieldNames = new HashMap<>();
    Stream.of(lawrence, gracie).forEach(person -> {
        valuesByFieldNames.computeIfAbsent("lastName", s -> new ArrayList<>()).add(person.getLastName());
        valuesByFieldNames.computeIfAbsent("firstName", s -> new ArrayList<>()).add(person.getFirstName());
    });

    assertThat(valuesByFieldNames, hasEntry("lastName", asList("Warren", "Ness")));
    assertThat(valuesByFieldNames, hasEntry("firstName", asList("Lawrence", "Gracie")));
}

2 个答案:

答案 0 :(得分:2)

您可以拥有以下功能,并行运行:

Map<String, List<String>> valuesByFieldNames = 
    Stream.of(lawrence, gracie).collect(HashMap::new, (m, p) -> {
        m.computeIfAbsent("lastName", s -> new ArrayList<>()).add(p.getLastName());
        m.computeIfAbsent("firstName", s -> new ArrayList<>()).add(p.getFirstName());
    }, (m1, m2) -> m2.forEach((k, v) -> m1.merge(k, v, (l1, l2) -> { l1.addAll(l2); return l1; })));

这样做是因为它将每个人收集到mutable HashMap。累加器通过调用computeIfAbsent来计算姓氏和名字,就像您的初始代码一样。组合器通过迭代第二个映射的条目并将每个键合并到第一个映射中来将两个映射合并在一起;如果发生冲突,则值为两个列表的添加。

答案 1 :(得分:2)

试试这个。

Map<String, List<String>> valuesByFieldNames = Stream.of(lawrence, gracie)
    .flatMap(p -> Stream.of(new String[]{"firstName", p.getFirstName()},
                            new String[]{"lastName", p.getLastName()}))
    .collect(Collectors.groupingBy(a -> a[0],
             Collectors.mapping(a -> a[1], Collectors.toList())));

或者更一般地说

Map<String, List<String>> valuesByFieldNames = Stream.of(lawrence, gracie)
    .flatMap(p -> Stream.of(new AbstractMap.SimpleEntry<>("firstName", p.getFirstName()),
                            new AbstractMap.SimpleEntry<>("lastName", p.getLastName())))
    .collect(Collectors.groupingBy(e -> e.getKey(),
             Collectors.mapping(e -> e.getValue(), Collectors.toList())));