有什么方法可以仅使用 `map` 而不是使用 `flatMap` 将二维列表转换为一维列表?

Is there any way to convert a 2D List to 1D List using only `map` not using `flatMap`?

我是 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。我总是追求简单和基本的东西。

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

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

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

回答这个问题,还有其他方法,但我不会推荐任何我能想到的。例如你可以使用 reduction:

    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() 不能互换吗?

Just because, if map can replaced all of flatMap, there are no reason to memorize flatMap. I always pursue simple and basic thing.

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

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

    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() 至少足够好,以便在我们需要时能够查找它们。

可能这不是您要查找的内容,但如果目标是将所有子列表添加到一个结果列表中,就 简单和基本而言,使用 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);

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

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

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

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

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

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]
}

您可以遍历此 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]