企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
# [第十四章 流式编程](https://lingcoder.gitee.io/onjava8/#/book/14-Streams?id=%e7%ac%ac%e5%8d%81%e5%9b%9b%e7%ab%a0-%e6%b5%81%e5%bc%8f%e7%bc%96%e7%a8%8b) > 集合优化了对象的存储,而流和对象的处理有关。 流是一系列与特定存储机制无关的元素——实际上,流并没有“存储”之说。 使用流,无需迭代集合中的元素,就可以从管道提取和操作元素。这些管道通常被组合在一起,形成一系列对流进行操作的管道。 在大多数情况下,将对象存储在集合中是为了处理他们,因此你将会发现你将把编程的主要焦点从集合转移到了流上。流的一个核心好处是,它使得程序更加短小并且更易理解。当 Lambda 表达式和方法引用(method references)和流一起使用的时候会让人感觉自成一体。流使得 Java 8 更具吸引力。 举个例子,假如你要随机展示 5 至 20 之间不重复的整数并进行排序。实际上,你的关注点首先是创建一个有序集合。围绕这个集合进行后续的操作。但是使用流式编程,你就可以简单陈述你想做什么: ~~~ // streams/Randoms.java import java.util.*; public class Randoms { public static void main(String[] args) { new Random(47) .ints(5, 20) .distinct() .limit(7) .sorted() .forEach(System.out::println); } } ~~~ 输出结果: ~~~ 6 10 13 16 17 18 19 ~~~ 首先,我们给**Random**对象一个种子(以便程序再次运行时产生相同的输出)。`ints()`方法产生一个流并且`ints()`方法有多种方式的重载 — 两个参数限定了产生的数值的边界。这将生成一个随机整数流。我们用中间流操作(intermediate stream operation)`distinct()`使流中的整数不重复,然后使用`limit()`方法获取前 7 个元素。接下来使用`sorted()`方法排序。最终使用`forEach()`方法遍历输出,它根据传递给它的函数对流中的每个对象执行操作。在这里,我们传递了一个可以在控制台显示每个元素的方法引用:`System.out::println`。 注意`Randoms.java`中没有声明任何变量。流可以在不使用赋值或可变数据的情况下对有状态的系统建模,这非常有用。 声明式编程(Declarative programming)是一种编程风格,它声明想要做什么,而非指明如何做。正如我们在函数式编程中所看到的。你会发现命令式编程的形式更难以理解。代码示例: ~~~ // streams/ImperativeRandoms.java import java.util.*; public class ImperativeRandoms { public static void main(String[] args) { Random rand = new Random(47); SortedSet<Integer> rints = new TreeSet<>(); while(rints.size() < 7) { int r = rand.nextInt(20); if(r < 5) continue; rints.add(r); } System.out.println(rints); } } ~~~ 输出结果: ~~~ [7, 8, 9, 11, 13, 15, 18] ~~~ 在`Randoms.java`中,我们无需定义任何变量,但在这里我们定义了 3 个变量:`rand`,`rints`和`r`。由于`nextInt()`方法没有下限的原因(其内置的下限永远为 0),这段代码实现起来更复杂。所以我们要生成额外的值来过滤小于 5 的结果。 注意,你必须用力的研究才能弄明白`ImperativeRandoms.java`程序在干什么。而在`Randoms.java`中,代码直接告诉了你它正在做什么。这种语义的清晰性是使用Java 8 流式编程的重要原因之一。 像在`ImperativeRandoms.java`中那样显式地编写迭代过程的方式称为外部迭代。而在`Randoms.java`中,你看不到任何上述的迭代过程,所以它被称为内部迭代,这是流式编程的一个核心特征。内部迭代产生的代码可读性更强,而且能更简单的使用多核处理器。通过放弃对迭代过程的控制,可以把控制权交给并行化机制。我们将在[并发编程](https://lingcoder.gitee.io/onjava8/#/24-Concurrent-Programming)一章中学习这部分内容。 另一个重要方面,流是懒加载的。这代表着它只在绝对必要时才计算。你可以将流看作“延迟列表”。由于计算延迟,流使我们能够表示非常大(甚至无限)的序列,而不需要考虑内存问题。