在一门关于Java函数式编程的初学者课程中,我看到了以下示例:
List.of("abc","def").stream().forEach(element -> System.out.println(element));
这是最基本的例子。在所有其他示例中,使用stream()函数的模式几乎相同。有人提到stream()是用来生成一个对象源的。
我的问题是我们需要流吗?
不能吗?:
List.of("abc","def").forEach(element -> System.out.println(element));
我需要帮助理解的另一件事是:;在函数编程中,函数的使用方式与数据的使用方式类似">
在上面的例子中,这意味着什么?
它是否指的是element -> System.out.println(element)
部分是如何以类似于数据的方式使用的?
我的问题是我们需要流吗?
不适用于此示例。forEach
是一片独特的雪花;它是唯一一个直接在列表中的,大多数其他流内容(map
、filter
、max
,诸如此类的内容)都只在流中。
更一般地说,不应使用forEach
。它完全不如普通的for循环。它在三个方面是不透明的:
- 它不是控制流透明的(不能从lambda内部中断/继续在lambda外部定义的循环;也不能返回包含lambda内部的方法)
- 它不是检查异常透明的(不能在lambda内部
throw
检查异常,即使lambda周围的上下文捕获了它们) - 它不是可变的透明局部变量:不能读取或写入lambda外部定义的任何局部变量,除非该变量(实际上)是
final
这些都是显著的缺点。在某些情况下,所有3个都不相关,但lambda也没有好处,因此,不要将list.forEach
与lambda一起使用。
列表上的forEach
本身是唯一,当您有一个来自其他地方的Consumer<T>
时,例如,在一个字段中,可以使用它。
可以使用流上的forEach
方法,但除非应用了一系列流操作,否则它没有任何意义。例如:
list.stream()
.filter(x -> x.length() > 6)
.map(String::toLowerCase()
.forEach(System.out::println)
;
这可能仍然不是一个好主意(只需使用for循环),但它没有那么可怕;必须使用闪亮的新锤子"作为CCD_ 13。
在函数编程中,函数的使用方式与数据的使用方式类似。
有点含糊不清。它通常指的是函数可以像变量一样传递的概念。例如,您可以编写一个函数来记录从字符串中删除了多少元素,并且您希望调用方传递给该函数";如何判断是否应该删除元素&";。你可以做什么:
public static <T> int removeIfCount(List<T> elems, Predicate<T> filter) {
int a = elems.size();
elems.removeIf(filter);
int b = elems.size();
return b - a;
}
这里的调用代码可以例如调用:
List<String> names = ...;
int count = removeIfCount(names, x -> x.length() > 6);
它会删除所有长度超过6个字符的名称,并统计删除的名称数量。在这里,我们像传递数据一样传递函数——我们的removeIfCount
代码只接受函数作为Predicate<T>
变量并传递它。就像传递数据的方式一样。
我将尝试解释Collections
和Streams
之间的区别,这在这里解释了
集合和流虽然有一些表面上的相似之处,但目标不同。藏品主要涉及对其元素的有效管理和访问。相比之下,流不提供直接访问或操作其元素的手段,而是关心声明性地描述其源以及将在该源上聚合执行的计算操作。
如果我们在这里检查,它可以帮助理解什么是Stream
,以及为什么它说
有人提到stream()用于生成对象源。