【问题标题】:Java group a map by value where value is a ListJava 按值对映射进行分组,其中值是列表
【发布时间】:2022-11-10 23:26:18
【问题描述】:

我有一个

Map<String,List<User>>map = new HashMap<>();
map.put("projectA",Arrays.asList(new User(1,"Bob"),new User(2,"John"),new User(3,"Mo")));
map.put("projectB",Arrays.asList(new User(2,"John"),new User(3,"Mo")));
map.put("projectC",Arrays.asList(new User(3,"Mo")));

可以使用字符串代替用户。

String 是一个项目名称,但相同的用户可以关联到不同的项目。

我想得到像Map&lt;User, List&lt;String&gt;&gt;这样的东西 键将代表一个不同的用户和一个值,作为与他/她相关的项目名称列表。

Bob  = [projectA]
John = [projectA, projectB]
Mo   = [projectA, projectB, projectC]

提前 TQ 以获得任何建议。

【问题讨论】:

    标签: java group-by hashmap java-stream


    【解决方案1】:

    只需遍历地图的条目和其中的列表:

    public static void main(String[] args) {
      Map<String, List<User>> map = new HashMap<>();
      map.put("projectA", Arrays.asList(new User(1,"Bob"),new User(2,"John"),new User(3,"Mo")));
      map.put("projectB",Arrays.asList(new User(2,"John"),new User(3,"Mo")));
      map.put("projectC",Arrays.asList(new User(3,"Mo")));
    
      Map<User, List<String>> result = new HashMap<>();
      for(Map.Entry<String, List<User>> e:map.entrySet()) {
        for(User u:e.getValue()) {
          result.putIfAbsent(u, new ArrayList<>());
          result.get(u).add(e.getKey());
        }
      }
      System.out.println(result);
    }
    public static record User(int id, String name) {}
    

    印刷

    {User[id=1, name=Bob]=[projectA], User[id=2, name=John]=[projectB, projectA], User[id=3, name=Mo]=[projectB, projectA, projectC]}
    

    【讨论】:

      【解决方案2】:

      要反转此 Map,您需要遍历其条目,并为每个不同的用户创建一个条目,其中包含项目列表作为结果 Map 中的值。

      Java 8 计算IfAbsent()

      这个逻辑可以使用 Java 8 方法 Map.computeIfAbsent()Map.forEach() 来实现。

      Map<String, List<User>> usersByProject = // initilizing the source map
      Map<User, List<String>> projectsByUser = new HashMap<>();
      
      usersByProject.forEach((project, users) ->
          users.forEach(user -> projectsByUser.computeIfAbsent(user, k -> new ArrayList<>())
               .add(project))
      );
      

      流 API

      基于流的实现需要更多的努力。

      核心逻辑保持不变。但是有一个重要的特性:我们需要从源 Map 的每个条目中生成一系列新元素,包含对特定用户和项目的引用。

      为了承载这些数据,我们需要一个辅助类型,Java 16 record 非常适合这个角色。快速而肮脏的替代方法是使用Map.Entry,但最好避免使用此选项,因为方法getKey()/getValue() 是匿名的,并且需要更多的努力来推理代码。如果您使用的是早期版本的 JDK,您还可以定义一个常规的 class

      public record UserProject(User user, String project) {}
      

      这就是基于流的解决方案的样子:

      Map<String, List<User>> usersByProject = Map.of(
          "projectA", List.of(new User(1, "Bob"), new User(2, "John"), new User(3, "Mo")),
          "projectB", List.of(new User(2, "John"), new User(3, "Mo")),
          "projectC", List.of(new User(3, "Mo"))
      );
          
      Map<User, List<String>> projectByUsers = usersByProject.entrySet().stream()
          .flatMap(entry -> entry.getValue().stream().
              map(user -> new UserProject(user, entry.getKey()))
          )
          .collect(Collectors.groupingBy(
              UserProject::user,
              Collectors.mapping(UserProject::project,
                  Collectors.toList())
          ));
              
      projectsByUser.forEach((k, v) -> System.out.println(k + " -> " + v));
      

      输出:

      User[id=1, name=Bob] -> [projectA]
      User[id=2, name=John] -> [projectA, projectB]
      User[id=3, name=Mo] -> [projectA, projectC, projectB]
      

      【讨论】:

        猜你喜欢
        • 2021-12-23
        • 1970-01-01
        • 2022-01-26
        • 1970-01-01
        • 2013-02-01
        • 1970-01-01
        • 1970-01-01
        • 2021-07-02
        • 1970-01-01
        相关资源
        最近更新 更多