准则一 λ 表达式和流
不要对函数对象使用匿名类,除非你必须创建非函数式接口类型的实例。
准则二 方法引用优于 λ 表达式
如果方法引用更短、更清晰,则使用它们;如果没有,仍然使用 lambda 表达式。
准则三 优先使用标准函数式接口
这个准则告诉我们优先使用标准的函数式接口。也就是在package java.util.function 中给我们定义了一些函数我们可以直接使用。
准则四 明智地使用流
流中的数据元素可以是对象的引用或基本数据类型。支持三种基本数据类型:int、long 和 double。
流管道的计算是惰性的:直到调用 Terminal 操作时才开始计算,并且对完成 Terminal 操作不需要的数据元素永远不会计算。这种惰性的求值机制使得处理无限流成为可能。
准则五 在流中使用无副作用的函数
forEach 操作应该只用于报告流计算的结果,而不是执行计算。
流范式中最重要的部分是将计算构造为一系列转换,其中每个阶段的结果都尽可能地接近上一阶段结果的纯函数。纯函数的结果只依赖于它的输入:它不依赖于任何可变状态,也不更新任何状态。为了实现这一点,传递到流操作(包括 Intermediate 操作和 Terminal 操作)中的任何函数对象都应该没有副作用。什么意思呢?大概是以下几点:
- 函数对象在执行过程中不应依赖于外部变量或状态。
- 如果函数对象需要访问某些数据,这些数据应该作为参数传入,而不是从外部环境获取。
- 函数对象不应该修改任何外部变量或状态。
- 所有的输入和输出都应该通过函数参数和返回值来传递。
流管道编程的本质是无副作用的函数对象。这适用于传递给流和相关对象的所有函数对象。Terminal 操作 forEach 只应用于报告由流执行的计算结果,而不应用于执行计算。为了正确使用流,你必须了解 collector。最重要的 collector 工厂是 toList、toSet、toMap、groupingBy 和 join。
准则六 优先选择 Collection 而不是流作为返回类型
Collection 接口是 Iterable 的一个子类型,它有一个流方法,因此它提供了迭代和流两种访问方式。因此,Collection 或其适当的子类通常是公共序列返回方法的最佳返回类型。 数组还提供了使用 Arrays.asList 和 Stream.of 方法进行简单迭代和流访问。如果返回的序列足够小,可以轻松地装入内存,那么最好返回标准集合实现之一,例如 ArrayList 或 HashSet。但是 不要将一个大的序列存储在内存中,只是为了将它作为一个集合返回。
准则七 谨慎使用并行流
并行性带来的性能提升在 ArrayList、HashMap、HashSet 和 ConcurrentHashMap 实例上的流效果最好;int 数组和 long 数组也在其中。
什么时候使用呢?如果你并行的每个流没有依赖关系就可以使用,也就是说每个流的结果是相互独立的,是不具有状态的。