Stream流粗解
- 声明性——更简洁,更易读
- 可复合——更灵活
- 可并行——性能更好
案例传送门:Stream流使用案例
Map<Dish.Type, List<Dish>> dishesByType = menu.stream().collection(groupingBy(Dish::getType));
流简介
简短定义:从支持数据处理操作的源生成的元素序列。
- 元素序列:类似于集合,流也提供了一个接口,可以访问特定元素类型的一组有序值。与集合不同,集合的主要目的是以特定的时间和空间复杂度存储和访问元素(如ArrayList和LinkedList)。流的主要目的在于表达计算。集合讲的是数据,流讲的是计算。
- 源: 流会使用一个提供数据的源,如集合、数组或输入/输出资源。从有序集合生成流时会保留原有的顺序。由列表生成的流,其元素顺序与列表一致。
- 数据处理操作:流的数据处理功能类似于数据库的操作,以及函数式编程语言的常用操作,如filter、map、reduce、find、match、sort等。流操作可以顺序执行,也可以并行执行
- 流水线:很多流操作本身会返回一个流,所以多个操作就可以链起来,形成一个流水线。
- 内部迭代:与使用迭代器显式迭代的集合不同,流的迭代操作是在背后进行的。
示例如下,结果为:[pork, beef, chicken]
List<String> threeHighCalorDishNames
= menu.stream()
.filter(d -> d.getCalories() > 300)
.map(Dish::getName)
.limit(3)
.collect(Collectors.toList());
System.out.println(threeHighCalorDishNames);
流与集合
集合:是一个内存中的数据结构,它包含数据结构中目前所有的值——集合中的每个元素都得先算出来才能添加到集合中。
流:是在概念上固定的数据结构(不能添加或删除元素),其元素时按需计算的,就像一个延迟创建的集合:在按需的一步步操作完成后才创建集合。
- 可比喻为在线流媒体,缓存一部分看一部分,按需操作。
- 也可比喻为用浏览器进行互联网搜索,假设搜索到多条匹配项,而可以得到一个流仅展示10个,点击下一页可展示接下来的10个,每次请求都是10个。
只能遍历一次
和迭代器类似,流只能遍历一次。遍历完成后,就称这个流已经被消费掉了。如下代码:
List<String> title = Arrays.asList("Java8", "Lambda", "Steam");
Stream<String> s = title.stream();
s.forEach(System.out::println);
s.forEach(System.out::println);
结果如图:只会遍历一次,第二次遍历就抛出了异常,说明流已经被消费掉了。
外部迭代与内部迭代
使用Collection接口需要用户去做迭代(比如for-each),这称为外部迭代。相反,Streams库使用内部迭代——它帮你把迭代做了,还把得到的流存在了某个地方,只需要给出函数说要干什么就可以了。如下分别是集合和流的代码,说明了这种区别。
List<String> names = new ArrayList<>();
for (Dish d : menu) {
names.add(d.getName());
}
List<String> names = new ArrayList<>();
Iterator<Dish> iterator = menu.iterator();
while (iterator.hasNext()) {
Dish d = iterator.next();
names.add(d.getName());
}
List<String> names = menu.stream()
.map(Dish::getName)
.collect(Collectors.toList());
集合:外部迭代,必须一个一个处理 你:“索菲亚,我们把玩具收起来吧。地上还有玩具吗?” 索菲亚:“有,球。” 你:“好,把球放进盒子里。还有吗?” 索菲亚:“有,娃娃。” 你:“好,把娃娃放进盒子里。还有吗?” … 索菲亚:“没了,没有了。” 你:“好,我们收完了。”
流:内部迭代,可以很好的并行处理,索菲亚可以一手球一手娃娃 你:“索菲亚,把地上的玩具都收起来。” 索菲亚:“好的。”
流操作
- 中间操作:从源获取流开始,可以进行多个类似于filter、sorted方法等中间操作,这些操作的结果还是流。
- 终端操作:经过一系列的中间操作后,最后使用一个终端方法,从流的流水线生成结果,该结果为其他非流的值,如List、Integer等,甚至可以是void。
- 使用流:
- 一个数据源(如集合)来执行一个查询;
- 一个中间操作链,形成一条流的流水线;
- 一个终端操作,执行流水线,并能生成结果。
筛选和切片
filter
Stream接口支持filter方法,该操作接受一个谓词作为参数,并返回一个包含符合谓词的元素的流。
List<Dish> vegetarianMenu = menu.stream()
.filter(Dish::isVegetarian)
.collect(toList());
distinct
Stream接口支持distinct方法,该操作会返回一个元素各异的流。
List<Integer> numbers = Arrays.asList(1,2,1,3,3,2,4);
numbers.stream()
.filter(i -> i % 2 == 0)
.distinct()
.forEach(System.out::println);
limit
Stream接口支持limit(n)方法,该方法返回一个不超过给定长度的流。
List<Dish> dishes = menu.stream()
.filter(d -> d.getCalories() > 300)
.limit(3)
.collect(toList());
skip
Stream接口支持skip(n)方法,与limit方法互补,返回一个丢掉前n元素的流。
List<Dish> dishes = menu.stream()
.filter(d -> d.getCalories() > 300)
.skip(2)
.collect(toList());
映射
Stream接口支持map和flatMap方法,接收一个函数作为参数,这个函数会作用到流的每个元素,并将其映射成一个新的元素。
map
flatMap
查找和匹配
anyMatch
allMatch
findAny
findFirst
规约
reduce
|