【问题标题】:Is there any way to convert a 2D List to 1D List using only `map` not using `flatMap`?有没有办法只使用`map`而不使用`flatMap`将2D列表转换为1D列表?
【发布时间】:2021-03-30 09:27:11
【问题描述】:

我是一名 Java 初学者,我刚刚了解了 mapflatMap

当 2d List 应该转换为 1d List 时,它的实现如下。

List<List<Integer>> list_2d = List.of(List.of(1, 2), List.of(3, 4));

List<Integer> lst1 = list_2d
    .stream()
    .flatMap(arr -> arr.stream())
    .collect(Collectors.toList());
printAll(lst1); // [1, 2, 3, 4]

但是,我认为它看起来可以不使用flatMap来实现。

有没有办法让代码具有相同的逻辑,只使用map,而不使用flatMap

只是问因为map是否可以替换所有flatMap,没有理由记住flatMap。我总是追求简单和基本的东西。

【问题讨论】:

  • 我不认为你可以在不使用 flatMap 的情况下将单个 Stream 元素转换为多个 Stream 元素。 map 仅将单个元素转换为不同类型的单个元素。
  • @OleV.V.正因为,如果map可以代替所有的flatMap,那么就没有理由去记住flatMap了。我总是追求简单而基本的东西。
  • @frhyme - I always pursue simple and basic thing. - flatMap 确实是一个简单而基本的东西。 Java 变得如此流行的原因之一是因为这些简单的构造/类型/操作;否则,在 C/C++ 中,我们不得不处理许多复杂性,因为没有可用的简单和基本的东西,例如。想想 Java 和 C/C++ 中的字符和字符串。

标签: java list java-stream flatmap


【解决方案1】:

要回答所提出的问题,还有其他方法,但我不会推荐任何我能想到的方法。例如,您可以使用归约:

    List<List<Integer>> list2d = List.of(List.of(1, 2), List.of(3, 4));

    List<Integer> lst1 = list2d
        .stream()
        .reduce((l1, l2) -> {
            ArrayList<Integer> concatenated = new ArrayList<>(l1);
            concatenated.addAll(l2);
            return concatenated;
        })
        .orElse(List.of()); // or else empty list
    
    System.out.println(lst1);

输出和你的一样:

[1, 2, 3, 4]

但是你的代码比我的更容易理解。我建议你坚持下去。

map() 和 flatMap() 不能互换吗?

只是因为,如果map可以取代所有的flatMap,那就没有理由了 记住 flatMap。我总是追求简单而基本的东西。

您已经掌握了最简单、最基本的东西。此外,为了充分发挥流的潜力,您需要不时使用许多方法调用。在使用流多年之后,我仍然发现自己有时会在 Javadoc 中查找它们。我必须查看reduce() 的详细信息才能得到这个答案。不要指望一切都在你的脑海中。不过,我确实知道flatMap(),因为它通常很实用。

编辑:仅出于学术兴趣:通常您不能将 flatMap() 替换为 map()。或者你会从一开始就使用map()。但反过来:你总是可以用flatMap() 替换map()。你只是不想。例如,如果我们有:

    List<String> strings = List.of("short", "somewhat longer");
    List<Integer> lengths = strings.stream()
            .map(String::length)
            .collect(Collectors.toList());
    System.out.println(lengths);

[5, 15]

如果出于某种奇怪的原因我们只能记住flatMap(),而不是map(),我们可以这样做:

    List<Integer> lengths = strings.stream()
            .flatMap(s -> Stream.of(s.length()))
            .collect(Collectors.toList());

我认为很明显,它给我们带来的只是不必要的复杂性。最好同时记住map()flatMap(),至少能够在需要时查找它们。

【讨论】:

    【解决方案2】:

    如果您在collect 期间执行平面映射,则可以删除flatMap

    List<Integer> lst1 = list_2d
        .stream()
        .collect(Collectors.flatMapping (List::stream, Collectors.toList()));
    

    但是,我没有看到使用 Collectors.flatMapping 代替 flatMap 的优势。

    【讨论】:

    • 你也可以直接使用reduce去掉Collectors.flatMapping
    【解决方案3】:

    这可能不是您要寻找的,但如果目标是将所有子列表添加到一个结果列表中,就简单和基本而言,使用 for each 也可能是一种选择:

    List<List<Integer>> list_2d = List.of(List.of(1, 2), List.of(3, 4));
    List<Integer> result = new ArrayList<>();
    list_2d.forEach(list -> result.addAll(list));
    System.out.println(result);
    

    【讨论】:

      【解决方案4】:

      由于已经有几个答案提供了不同的实现,我将尝试展示mapflatMap 之间的区别。

      map 操作用于将流中的每个元素都更改为另一个元素。在您的示例中,可能的用例可能是对列表进行一些聚合(例如,对所有元素求和),产生一个包含各个列表的所有总和的 List&lt;Integer&gt;。或者你可以改变数据结构,也许是一个集合,产生一个List&lt;Set&lt;Integer&gt;&gt;。结果列表中的元素数将始终为 2,因为原始列表有 2 个元素。

      另一方面,flatMap 操作用于将一个元素转换为零个或多个元素。考虑一个由许多人类组成的类家庭。如果您从某个地方获得了家庭列表,但您只关心个人,您可以 flatMap 家庭列表到人类列表。

      flatMap 与其他答案中给出的实现之间的区别在于操作类型:flatMap 是中间操作,这意味着结果是Streamcollectreduce 是终端操作,这意味着结果有所不同(在您的示例中:List&lt;Integer&gt;。如果您想执行更多流操作(例如 filter 或另一个 map),您将拥有为结果列表创建另一个 Stream

      为了说明这一点,请考虑在您的问题中给出列表,因此您希望获得列表中的所有偶数。我正在使用来自this 答案的collect 解决方案。如您所见,使用 flatMap 可以节省不必要的 List 的创建以及一些代码行。

      public static void usingFlatMap() {
          List<List<Integer>> originalList = Arrays.asList(Arrays.asList(1, 2), Arrays.asList(3, 4));
      
          List<Integer> result = originalList
                  .stream()
                  .flatMap(list -> list.stream())
                  .filter(number -> number % 2 == 0)
                  .collect(Collectors.toList());
      
          System.out.println(result); // [2, 4]
      }
      
      public static void usingCollect() {
          List<List<Integer>> originalList = Arrays.asList(Arrays.asList(1, 2), Arrays.asList(3, 4));
      
          List<Integer> intermediateList = originalList
                  .stream()
                  .collect(Collectors.flatMapping(List::stream, Collectors.toList()));
          List<Integer> result = intermediateList
                  .stream()
                  .filter(number -> number % 2 == 0)
                  .collect(Collectors.toList());
      
          System.out.println(result); // [2, 4]
      }
      

      【讨论】:

        【解决方案5】:

        您可以遍历这个 2d-list 并将其中的元素(整数)添加到另一个 1d-list


        1. List<List<Integer>> list_2d = List.of(List.of(1, 2), List.of(3, 4));
          
          List<Integer> list_1d = new ArrayList<>();
          
          IntStream.range(0, list_2d.size()).forEach(i ->
                  IntStream.range(0, list_2d.get(i).size()).forEach(j ->
                          list_1d.add(list_2d.get(i).get(j))));
          
          System.out.println(list_1d); // [1, 2, 3, 4]
          

        1. List<List<Integer>> list_2d = List.of(List.of(1, 2), List.of(3, 4));
          
          List<Integer> list_1d = new ArrayList<>();
          
          list_2d.forEach(list ->
                  list.forEach(element ->
                          list_1d.add(element)));
          
          System.out.println(list_1d); // [1, 2, 3, 4]
          

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2022-11-03
          • 2019-02-03
          • 2014-10-13
          • 2013-07-23
          • 2011-01-12
          • 2020-05-02
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多