一、流(Stream)简介
流是 Java8 中 API 的新成员,它允许你以声明式的方式处理数据集合(通过查询语句来表达,而不是临时编写一个实现)。这有点儿像是我们操作数据库一样,例如我想要查询出热量较低的菜品名字我就可以像下面这样:
SELECT name FROM dishes WHERE calorie < 400;
您看,我们并没有对菜品的什么属性进行筛选(比如像之前使用迭代器一样每个做判断),我们只是表达了我们想要什么。那么为什么到了 Java 的集合中,这样做就不行了呢?
另外一点,如果我们想要处理大量的数据又该怎么办?是否是考虑使用多线程进行并发处理呢?如果是,那么可能编写的关于并发的代码比使用迭代器本身更加的复杂,而且调试起来也会变得麻烦。
基于以上的几点考虑,Java 设计者在 Java 8 版本中,引入了流的概念,来帮助您节约时间!并且有了 lambda 的参与,流操作的使用将更加顺畅!
特点一:内部迭代
就现在来说,您可以把它简单的当成一种高级的迭代器(Iterator),或者是高级的 for 循环,区别在于,前面两者都是属于外部迭代,而流采用内部迭代。
上图简要说明了内部迭代与外部迭代的差异,我们再举一个生活中实际的例子(引自《Java 8 实战》),比如您想让您两岁的孩子索菲亚把她的玩具都收到盒子里面去,你们之间可能会产生如下的对话:
- 你:“索菲亚,我们把玩具收起来吧,地上还有玩具吗?”
- 索菲亚:“有,球。”
- 你:“好,把球放进盒子里面吧,还有吗?”
- 索菲亚:“有,那是我的娃娃。”
- 你:“好,把娃娃也放进去吧,还有吗?”
- 索菲亚:“有,有我的书。”
- 你:“好,把书也放进去,还有吗?”
- 索菲亚:“没有了。”
- 你:“好,我们收好啦。”
这正是你每天都要对 Java 集合做的事情。你外部迭代了一个集合,显式地取出每个项目再加以处理,但是如果你只是跟索菲亚说:“把地上所有玩具都放进盒子里”,那么索菲亚就可以选择一手拿娃娃一手拿球,或是选择先拿离盒子最近的那个东西,再拿其他的东西。
采用内部迭代,项目可以透明地并行处理,或者用优化的顺序进行处理,要是使用 Java 过去的外部迭代方法,这些优化都是很困难的。
这或许有点鸡蛋里挑骨头,但这差不多就是 Java 8 引入流的原因了——Streams 库的内部迭代可以自动选择一种是和你硬件的数据表示和并行实现。
特点二:只能遍历一次
请注意,和迭代器一样,流只能遍历一次。当流遍历完之后,我们就说这个流已经被消费掉了,你可以从原始数据那里重新获得一条新的流,但是却不允许消费已消费掉的流。例如下面代码就会抛出一个异常,说流已被消费掉了:
List title = Arrays.asList("Wmyskxz