Java 8中的流如何收集一个项目列表的映射?

30

我有一个存储人员角色和姓名的地图列表。例如:

List<Map<String, String>> listOfData

1) Role:  Batsman
   Name:  Player1

2)Role:  Batsman
   Name:  Player2

3)Role:  Bowler
   Name:  Player3

角色和名称是该映射表的键。我希望将其转换为Map<String,List<String>> result,这将为每个角色提供一个名称列表,即:

Map<String, List<String>> result
k1: Batsman  v1: [Player1, Player2]
k2: Bowler   v2: [Player3]


listOfData
    .stream()
    .map(entry -> new AbstractMap.SimpleEntry<>(entry.get("Role"), entry.get("Name"))
    .collect(Collectors.toList());

这样做不会给我一个角色名称列表,它只会给我一个名字。如何保持收集列表元素并将其添加到键的状态?

创建基本结构的Java代码:

Map<String, String> x1 = ImmutableMap.of("Role", "Batsman", "Name", "Player1");

        Map<String, String> y1 = ImmutableMap.of("Role", "Batsman", "Name", "Player2");

        Map<String, String> z1 = ImmutableMap.of("Role", "Bowler", "Name", "Player3");


        List<Map<String, String>> list = ImmutableList.of(x1, y1, z1);
        Map<String, List<String>> z = list.stream()
                    .flatMap(e -> e.entrySet().stream())
                    .collect(Collectors.groupingBy(Map.Entry::getKey,
                            Collectors.mapping(Map.Entry::getValue, Collectors.toList())));

1
https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html#groupingBy-java.util.function.Function-java.util.stream.Collector- - JB Nizet
4个回答

35
listOfData.stream()
          .flatMap(e -> e.entrySet().stream())
          .collect(Collectors.groupingBy(Map.Entry::getKey, 
                         Collectors.mapping(Map.Entry::getValue, 
                                    Collectors.toList())));

更新:

为了完整起见,这里提供一个与user1692342的答案略有不同的变体。

list.stream()
    .map(e -> Arrays.asList(e.get("Role"), e.get("Name")))
    .collect(Collectors.groupingBy(e -> e.get(0),
             Collectors.mapping(e -> e.get(1), Collectors.toList())));

这里应该按照角色和名字分组,比如: K1: 角色 V1: 打手,投手 K2: 名字 V2: 球员1, 球员2, 球员3 - user1692342
这个对输入无效。更新问题,附带Java代码示例。 - user1692342
在我回答这个问题的时候,我实际上认为你的地图是像 Map<String, String> x1 = Map.of( "Batsman", "Player1"); Map<String, String> y1 = Map.of( "Batsman", "Player2"); 这样的,看起来你在我回来之前找到了缺失的操作 :) +1。 - Ousmane D.
1
是的,主要的技巧是在collect中使用Collectors.toMapping。我之前不知道这一点。谢谢你! - user1692342
@user1692342,不用谢,为了完整性请更新我的答案。 - Ousmane D.

20

根据Aomine提出的思路:

list.stream()
    .map(e -> new AbstractMap.SimpleEntry<>(e.get("Role"), e.get("Name")))
    .collect(Collectors.groupingBy(Map.Entry::getKey,
                    Collectors.mapping(Map.Entry::getValue, Collectors.toList())));

14

Collectors 类提供了方便的方法,如 groupingBy,允许通过某个分类器将相似的对象分组。分类器方法是传递给特定分组函数的输入。此函数将生成一个 Map,其中各自的分类器方法值作为键,共享相同分类器方法值的对象列表作为值。

因此,像这样的代码

Map<String, List<Person>> roles2Persons = 
    lis.stream().collect(Collectors.groupingBy(Person::getRole));

将为各自的角色生成一个映射,Person对象可能会完成与该映射中键所表示的角色相同的Person对象列表。

应用上述收集器后,生成的映射将包含所需形式的内容。

key1: Batsman, values: List(Player1, Player2)
key2: Bowler, values: List(Player3)

4
虽然这段代码可能回答了问题,但如果提供关于为什么和/或如何回答问题的额外背景信息,可以提高其长期价值。 - xiawi

0

这是解决此问题的通用方法。下面的groupBy方法需要2到3个参数。

  1. 类型为List<E>collection
  2. 类型为Function<E, K>keyFn(键)
  3. (可选)类型为Function<E, V>或简单的EvalueFn(值)

我在下面包含了一些单元测试。

CollectionUtils.java

package org.example.util;

import static java.util.Collections.unmodifiableMap;
import static java.util.stream.Collectors.*;

import java.util.*;
import java.util.AbstractMap.SimpleEntry;
import java.util.Map.Entry;
import java.util.function.Function;

public class CollectionUtils {
  public static <E, K, V> Map<K, List<V>> groupBy(
      Collection<E> collection, Function<E, K> keyFn, Function<E, V> valueFn) {
    return collection.stream()
        .map(item -> new SimpleEntry<K, V>(keyFn.apply(item), valueFn.apply(item)))
        .collect(groupingBy(Entry::getKey, mapping(Entry::getValue, toList())));
  }

  public static <E, K> Map<K, List<E>> groupBy(Collection<E> collection, Function<E, K> keyFn) {
    return groupBy(collection, keyFn, Function.identity());
  }

  public static <K, V> Map<K, V> immutableMapOf(K k1, V v1, K k2, V v2) {
    Map<K, V> mutableMap = new HashMap<>();
    mutableMap.put(k1, v1);
    mutableMap.put(k2, v2);
    return unmodifiableMap(mutableMap);
  }
}

CollectionUtilsTest.java

package org.example.util;

import static java.util.Arrays.asList;
import static org.example.util.CollectionUtils.*;
import static org.junit.Assert.*;

import java.util.*;
import org.junit.Test;
import org.slf4j.*;

public class CollectionUtilsTest {

  private static final Logger logger = LoggerFactory.getLogger(CollectionUtilsTest.class);

  private static final List<Map<String, String>> data =
      asList(
          immutableMapOf("Username", "Batman", "Role", "Leader"),
          immutableMapOf("Username", "Robin", "Role", "Subordinate"),
          immutableMapOf("Username", "Superman", "Role", "Leader"));

  @Test
  public void testGroupBy() {
    logger.info("Test groupBy(Collection<E>, Function<E, K>, Function<E, V>)");
    Map<String, List<String>> grouped = groupBy(data, m -> m.get("Role"), m -> m.get("Username"));

    logger.info("Checking keys...");
    assertNotNull(grouped.get("Leader"));
    assertNotNull(grouped.get("Subordinate"));

    logger.info("Checking values...");
    assertEquals("Batman", grouped.get("Leader").get(0));
    assertEquals("Superman", grouped.get("Leader").get(1));
    assertEquals("Robin", grouped.get("Subordinate").get(0));
  }

  @Test
  public void testGroupBySimple() {
    logger.info("Test groupBy(Collection<E>, Function<E, K>)");
    Map<String, List<Map<String, String>>> grouped = groupBy(data, m -> m.get("Role"));

    logger.info("Checking keys...");
    assertNotNull(grouped.get("Leader"));
    assertNotNull(grouped.get("Subordinate"));

    logger.info("Checking values...");
    assertEquals("Batman", grouped.get("Leader").get(0).get("Username"));
    assertEquals("Superman", grouped.get("Leader").get(1).get("Username"));
    assertEquals("Robin", grouped.get("Subordinate").get(0).get("Username"));
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接