Java8实战-总结16

Java8实战-总结16

  • 引入流
    • 流与集合
      • 只能遍历一次
      • 外部迭代与内部迭代

引入流

流与集合

只能遍历一次

和迭代器类似,流只能遍历一次。遍历完之后,这个流就已经被消费掉了。可以从原始数据源那里再获得一个新的流来重新遍历一遍,就像迭代器一样(这里假设它是集合之类的可重复的源,如果是I/O通道就没戏了)。例如,以下代码会抛出一个异常,说流已被消费掉了:

List<String> title = Arrays.asList("Java8","In","Action");
Stream<String> s = title.stream();
//打印标题中的每个单词
s.forEach(System.out::println);
//java.lang.IllegalStateException:流已被操作或关闭
s.forEach(System.out::println);

所以要记得,流只能消费一次。

对于喜欢哲学的读者,你可以把流看作在时间中分布的一组值。相反,集合则是空间(这里就是计算机内存)中分布的一组值,
在一个时间点上全体存在——你可以使用迭代器来访问for-each循环中的内部成员。

集合和流的另一个关键区别在于它们遍历数据的方式。

外部迭代与内部迭代

使用Collection接口需要用户去做迭代(比如用for-each),这称为外部迭代。相反,Streams库使用内部迭代——它帮你把迭代做了,还把得到的流值存在了某个地方,你只要给出一个函数说要干什么就可以了。下面的代码列表说明了这种区别:

//集合:用for-each循环外部迭代
	List<String> names = new ArrayList<>();
	for(Dish d : menu) { //显式顺序迭代菜单列表
		names.add(d.getName());//提取名称并将其添加到累加器
	}

请注意,for-each还隐藏了迭代中的一些复杂性。for-each结构是一个语法糖,它背后的东西用Iterator对象表达出来更要丑陋得多。

//集合:用背后的迭代器做外部迭代
	List<String> names = new ArrayList<>();
	Iterator<String> iterator = menu.iterator();
	while(iterator.hasNext()) {//显式迭代
		Dish d = iterator.next();
		names.add(d.getName());
	}
//流:内部迭代
List<String> names = menu.stream()
						.map(Dish::getName)//用getName方法参数化map,提取菜名
						.collect(toList());//开始执行操作流水线;没有迭代!
用一个比喻来解释内部迭代的差异和好处吧。比方说你在和索菲亚说话,希望她能把玩具收起来。
你:“索菲亚,我们把玩具收起来吧。地上还有玩具吗?”
索菲亚:“有,球。”
你:“好,把球放进盒子里。还有吗?”
索菲亚:“有,那是我的娃娃。”
你:“好,把娃娃放进盒子里。还有吗?”
索菲亚:“有,有我的书。”
你:“好,把书放进盒子里。还有吗?”
索菲亚:"没了,没有了。"
你:“好,我们收好啦。”

这正是每天都要对Java集合做的。外部迭代一个集合,显式地取出每个项目再加以处理。如果只需跟索菲亚说“把地上所有的玩具都放进盒子里”就好了。内部迭代比较好的原因有二:
第一,索非亚可以选择一只手拿娃娃,另一只手拿球;第二,她可以决定先拿离盒子最近的那个东西,然后再拿别的。同样的道理,内部迭代时,项目可以透明地并行处理,或者用更优化的顺序进行处理。要是用Java过去的那种外部迭代方法,这些优化都是很困难的。这似乎有点儿鸡蛋里挑骨头,但这差不多就是Java8引入流的理由了——Streams库的内部迭代可以自动选择一种适合你硬件的数据表示和并行实现。与此相反,一旦通过写for-each而选择了外部迭代,那基本上就要自己管理所有的并行问题了(自己管理实际上意味着“某个良辰吉日我们会把它并行化”或“开始了关于任务和synchronized的漫长而艰苦的斗争”)。Java 8需要一个类似于Collection却没有迭代器的接口,于是就有了stream。下图说明了流(内部迭代)与集合(外部迭代)之间的差异:
Java8实战-总结16_第1张图片
内部迭代与外部迭代

已经说过了集合与流在概念上的差异,特别是流利用了内部迭代:替你把迭代做了。但是,只有已经预先定义好了能够隐藏迭代的操作列表,例如filtermap,这个才有用。大多数这类操作都接受Lambda表达式作为参数,因此可以用前面介绍的方法来参数化其行为。Java语言的设计者给Stream API配上了一大套可以用来表达复杂数据处理查询的操作。

你可能感兴趣的:(开发语言,java)