Java函数式编程

8wtpewkr  于 2023-04-10  发布在  Java
关注(0)|答案(2)|浏览(87)

在一个关于Java函数式编程的初学者课程中,我看到了下面的例子:

List.of("abc","def").stream().forEach(element -> System.out.println(element));

这是最基本的例子。在所有其他例子中,使用stream()函数的模式几乎相同。前面提到stream()是用来生成对象源的。
我的问题是我们需要流吗?
Cant it be?:

List.of("abc","def").forEach(element -> System.out.println(element));

我需要帮助理解的另一件事是声明:在函数式编程中,函数的使用方式与数据的使用方式类似。
在上面的例子中,这意味着什么?
它是否指的是element -> System.out.println(element)部分如何以类似于数据的方式使用?

lokaqttq

lokaqttq1#

我的问题是我们需要流吗?
本例中没有。forEach是唯一的 snowflake ;它是唯一一个直接在列表中的,大多数其他流的东西(mapfiltermax,这类东西)只在流中。
更一般地说,forEach永远不应该使用,它比普通的for循环差很多,它在三个方面是不透明的:

  • 它不是控制流透明的(你不能从lambda内部中断/继续定义在lambda外部的循环;你也不能从一个内部返回包含lambda的方法)。
  • 它不是检查异常透明的(即使lambda周围的上下文捕获了检查异常,也不能在lambda内部throw检查异常)。
  • 它不是可变的局部变量透明的:你不能读取或写入任何在lambda之外定义的局部变量,除非该变量(实际上)是final

这些都是显著的缺点。在某些情况下,所有3个都是不相关的,但lambdas也没有好处,因此,不要将list.forEach与lambda一起使用。
forEach on list本身 * 仅 * 在您已经从其他地方(例如,在某个字段中)获得Consumer<T>时使用。
可以在流上使用forEach方法,但除非您应用了一系列流操作,否则它没有任何意义。例如:

list.stream()
  .filter(x -> x.length() > 6)
  .map(String::toLowerCase()
  .forEach(System.out::println)
  ;

可能仍然不是一个好主意(只需使用for循环),但它不像list.forEach(x ->...)那样过分地“必须...使用...闪亮的...新的...锤子!”。
在函数式编程中,函数的使用方式类似于数据的使用方式。
这是一个有点模糊的声明。它通常指的是函数可以像变量一样传递的概念。例如,你可以写一个函数来记录从字符串中删除了多少元素,你希望调用者向这个函数传递“我如何判断一个元素是否应该被删除?”。你可以这样做:

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>变量并沿着。就像传递数据一样。

dbf7pr2w

dbf7pr2w2#

我将试着解释CollectionsStreams之间的区别,这解释了here
集合和流虽然有一些表面上的相似之处,但有不同的目标。集合主要关注对其元素的有效管理和访问。相比之下,流不提供直接访问或操作其元素的方法,而是关注声明性地描述其源和将在该源上聚合执行的计算操作。
另外,如果我们检查这里,它可以帮助理解什么是Stream,以及为什么它说
前面提到,stream()是用来生成对象的源。

相关问题