问题描述
我是一名 Java 初学者,我刚刚了解了 map
和 flatMap
。
当 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
。我总是追求简单和基本的东西。
解决方法
要回答所问的问题,还有其他方法,但我不会推荐我能想到的任何方法。例如你可以使用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() 不能互换吗?
仅仅因为,如果 map 可以替代所有 flatMap,那就没有理由了 记住 flatMap。我总是追求简单和基本的东西。
您已经掌握了最简单、最基本的东西。此外,为了充分发挥流的潜力,您需要不时使用许多方法调用。在使用流多年之后,我仍然发现自己有时会在 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()
,以便在需要时能够查找它们。
如果您在 flatMap
期间执行平面映射,您可以删除 collect
:
List<Integer> lst1 = list_2d
.stream()
.collect(Collectors.flatMapping (List::stream,Collectors.toList()));
但是,我看不到使用 Collectors.flatMapping
代替 flatMap
的优势。
可能这不是您要查找的内容,但是如果目标是将所有子列表添加到一个结果列表中,则使用 for each 也可能是简单和基本的一种选择:
List<List<Integer>> list_2d = List.of(List.of(1,4));
List<Integer> result = new ArrayList<>();
list_2d.forEach(list -> result.addAll(list));
System.out.println(result);
,
由于已经有几个答案提供了不同的实现,我将尝试展示 map
和 flatMap
之间的区别。
map
操作用于将流的每个元素更改为恰好另一个元素。在您的示例中,可能的用例可能是对列表进行一些聚合(例如,对所有元素求和),产生一个 List<Integer>
包含单个列表的所有总和。或者您可以更改数据结构,可能更改为一个集合,产生一个 List<Set<Integer>>
。结果列表中的元素数将始终为 2,因为原始列表有 2 个元素。
另一方面,flatMap
操作用于将一个元素转换为零个或多个元素。考虑一个由许多人类组成的类 Family。如果您从某处获得家庭列表,但您只关心个人,则可以将家庭列表flatMap
转换为人类列表。
flatMap
与其他答案中给出的实现之间的区别在于操作类型:flatMap
是中间操作,意味着结果是 Stream
。 collect
和 reduce
是终端操作,意味着结果有所不同(在您的示例中:List<Integer>
。如果您想执行更多流操作(例如 filter
或另一个 map
) 您必须创建结果列表的另一个 Stream
。
为了说明这一点,请考虑在您的问题中给出列表,因此您希望列表中的所有偶数都包含在内。我正在使用 this 答案中的 collect
解决方案。如您所见,使用 flatMap
可以节省不必要的 List 创建以及一些代码行。
public static void usingFlatMap() {
List<List<Integer>> originalList = Arrays.asList(Arrays.asList(1,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,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:
-
List<List<Integer>> list_2d = List.of(List.of(1,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,4]
-
List<List<Integer>> list_2d = List.of(List.of(1,4)); List<Integer> list_1d = new ArrayList<>(); list_2d.forEach(list -> list.forEach(element -> list_1d.add(element))); System.out.println(list_1d); // [1,4]