当前位置: 首页 > 知识库问答 >
问题:

使用Java8个流将数据分组到地图中

咸皓
2023-03-14

我想使用Java8 streams API对数据进行分组。所有具有父_id的行都应分组在一起。下面是示例文本文件。结果应该是一个映射,其中id是一个整数,值是各自分组的行。例如,在下面的例子中,结果将是一个包含3个条目的映射。键1对应2个值,键2对应无值,键3对应1个值。

id      name    parent_id
1       A       (null)
2       B       1
3       C       1
4       D       (null)
5       E       (null)
6       F       5

代码片段是:

Map<String, List<FileVO>> map= list.stream()
        .collect(groupingBy(FileVO::getParentId, toList()));

输出可以是:{A,{B, C}},{D,{}},{E,{F}}

简单的规则是:如果父母ID不是空的,这些记录应该被分组到一个列表中。这个列表将被视为map中的值。它的键将是父母ID,这是实际的id(列id的值,它不会为空。而父母ID可以为空。如果一条记录的父母ID为空,而其他记录的父母ID列中没有其他记录的ID,那么它将被视为一个具有键但为空值的单一对象。)

共有2个答案

范凡
2023-03-14

以下是一个更复杂的解决方案:

在第一个group By()中,如果可用,则使用家长ID,如果不可用,则使用id

Map<Integer, List<FileVO>> result = list.stream()
        .collect(Collectors.groupingBy(f -> Optional.ofNullable(f.getParentId()).orElse(f.getId())));

这将创建属于一起的文件组:

{
  1: [
       {id: 1, name: "A", parentId: null},
       {id: 2, name: "B", parentId: 1},
       {id: 3, name: "C", parentId: 1}
  ],
  4: [
       {id: 4, name: "D", parentId : null}
  ],
  5: [
       {id: 5, name: "E", parentId : null},
       {id: 6, name: "F", parentId : 5}
  ]
}

在第二步中,您将在每个组中找到父元素。如果您可以确保每个列表中的第一个元素都是父元素(就像在您的示例中,您可以使用以下内容:

Map<String, List<String>> result = list.stream()
        .collect(Collectors.groupingBy(f -> Optional.ofNullable(f.getParentId()).orElse(f.getId())))
        .entrySet().stream()
        .collect(Collectors.groupingBy(e -> e.getValue().get(0).getName(),
                Collectors.flatMapping(e -> e.getValue().stream().skip(1).map(FileVO::getName), Collectors.toList())));

这将只获取第一个元素(父元素)的名称,并映射除父元素本身之外的所有元素的名称。

如果您无法确保需要更通用的解决方案:

Map<String, List<String>> result = list.stream()
        .collect(Collectors.groupingBy(f -> Optional.ofNullable(f.getParentId()).orElse(f.getId())))
        .entrySet().stream()
        .collect(Collectors.groupingBy(
                e -> e.getValue().stream().filter(f -> f.getId() == e.getKey()).findAny().map(FileVO::getName).orElseThrow(),
                Collectors.flatMapping(
                        e -> e.getValue().stream().filter(f -> f.getId() != e.getKey()).map(FileVO::getName),
                        Collectors.toList())));

这实际上是相同的,但是通过使用我们之前创建的映射条目的键来搜索父元素。

这两种解决方案都会将其作为示例数据返回:

{
  A: [B, C],
  D: [],
  E: [F]
}
姜烨伟
2023-03-14

我认为你不能单流做。

Map<Integer, String> roots = list.stream()
            .filter(myObject -> myObject.getParentId() == null)
            .collect(Collectors.toMap(MyObject::getId, MyObject::getName));

out put的id和名称均为父级

{1=A,4=D,5=E}

Map<Integer, List<String>> groupByParentId = list.stream()
            .filter(myObject -> myObject.getParentId() != null)
            .collect(Collectors.groupingBy(MyObject::getParentId,
                    Collectors.mapping(MyObject::getName, toList())));

输出按parentId分组

{1=[B,C],5=[F]}

最后一步是:

roots.forEach((k,v)->map.put(v,groupByParentId.getOrDefault(k,new ArrayList<>())));

流版本更新:复杂性为O(n^2)

  list.stream()
            .filter(myObject -> myObject.getParentId() == null)
            .collect(Collectors.toMap(MyObject::getName, MyObject::getId))
            .forEach((k, v) -> map.put(k, list.stream()
            .filter(myObject -> myObject.getParentId() == v)
            .map(MyObject::getName)
            .collect(Collectors.toList())));

或者你也可以使用非流的方式,比如:(我个人更喜欢非流版本)

注意:这样,根就是Map

String root = "";
for (MyObject myObject : list) {
    if (myObject.getParentId() == null) {
       root = myObject.getName();
       map.put(root, new ArrayList<>());
    }
    if (roots.get(root).equals(myObject.getParentId())){
      map.computeIfAbsent(root, k -> new ArrayList<>()).add(myObject.getName());
    }
}

 类似资料:
  • 问题内容: 我需要将Java转换为的实例(包括地图内容) 我应该怎么做才能使此代码可编译? 问题答案: 从Collectors.toMap(…)javadoc: 例如:

  • 我正在使用我有一个类,如下所示: 现在我想做的是: 筛选出senderId无效的记录(使用映射) 下面是我的代码: 这给我带来了一个错误: 错误:(105,90)java:找不到适用于groupingBy(共享[…]的方法gMode,java。util。作用函数)方法java。util。流动收藏家。groupingBy(java.util.function.function)不适用(无法推断类型变

  • 我正在尝试创建一个简单的解析util,它转换一个两列CSV文件并将其放入一个映射。 如您所见,我正在创建一个字符串流,用逗号分隔每一行,并将其转换为字符串数组,最后将键映射到索引0,将值映射到索引1。 出于某种原因,当我运行这个测试时,实际值为null。我排除了无效的文件路径,因为它在另一个单元测试中运行良好,并且键值出现在CSV中。我已经盯着它看了几个小时了,我想也许有人能指出我的错误。 此外,

  • 我是Java8的新手,不能使用流将一个数组映射到另一个二维数组。 和包含键的第二个数组。 0表示:从模式中取0元素 1表示:从模式中取1元素,以此类推 从这两个数组中,我想生成另一个二维数组。在这种情况下,结果如下所示: 请帮忙

  • 我试图翻译这个(简化)代码使用Java-8流: 以下是我尝试过的: 但以上给出了所有

  • 我正在用Stream学习Java,我有一张