一. 流的概念java
流是Java API的新成员,它容许你以声明性的方式处理数据集合(经过查询语句来表达,而不是临时编写一个实现)。数据结构
Stream API的特性:app
-- 声明性:更简洁,更易读;ide
-- 可复合:更灵活;性能
-- 可并行:性能更好。优化
1.流的定义:从支持数据处理操做的源生成的元素序列。this
流有两个重要特色:spa
-- 流水线:不少流操做自己会返回一个流,这样多个操做就能够连接起来,造成一个大的流水线;接口
-- 内部迭代:与使用迭代器显式迭代的集合不一样,流的迭代操做是在背后进行的。three
案例:
public class Dish { private final String name; private final int calories; private final boolean vegetarian; private final DishType type; public Dish(String name, boolean vegetarian, int calories, DishType type) { this.name = name; this.vegetarian = vegetarian; this.calories = calories; this.type = type; } public String getName() { return name; } public int getCalories() { return calories; } public boolean isVegetarian() { return vegetarian; } }
public static void main(String[] args) { List<Dish> menu = Arrays.asList( new Dish("pork", false, 800, DishType.MEAT), new Dish("prawns", false, 300, DishType.FISH), new Dish("chicken", false, 700, DishType.MEAT), new Dish("rice", true, 350, DishType.OTHER) ); List<String> threeHighCaloricDishNames = menu.stream() //菜单流Stream<Dish> .filter(d -> d.getCalories() > 300) //Stream<Dish> .map(Dish::getName) //Stream<String> .limit(3) //Stream<String> .collect(toList()); //List<String>,collect操做开始处理流水线 System.out.println(threeHighCaloricDishNames); //结果:[pork, chicken, rice] }
数据源:menu,它给流提供一个元素序列,而后对流应用一系列数据处理操做:filter、map、limit和collect。除了collect以外,全部这些操做都会返回一个流,这样它们就能够接成一条流水线,因而就能够当作对源的一个查询。最后collect开始处理流水线,并返回结果。在调用collect以前,没有任何结果产生,实际上根本没有从menu里选择元素。能够理解为:链中的方法都在排队等待,直到调用collect。
2.流操做
流操做分为中间操做和终端操做。能够链接起来的流操做称为中间操做,关闭流的操做称为终端操做。
中间操做:中间操做会返回另外一个流。这让多个操做能够链接起来造成一个查询。
除非流水线上触发一个终端操做,不然中间操做不会执行任何处理。这是由于中间操做通常均可以合并起来,在终端操做时一次性所有处理。
终端操做:终端操做会从流的流水线生成结果。其结果是任何不是流的值。
public static void main(String[] args) { List<Dish> menu = Arrays.asList( new Dish("pork", false, 800, DishType.MEAT), new Dish("prawns", false, 300, DishType.FISH), new Dish("chicken", false, 700, DishType.MEAT), new Dish("rice", true, 350, DishType.OTHER) ); List<String> names = menu.stream() .filter(d -> { System.out.println("filtering " + d.getName()); return d.getCalories() > 130; }) .map(d -> { System.out.println("mapping " + d.getName()); return d.getName(); }) .limit(3) .collect(toList()); System.out.println(names); } 输出结果: filtering pork mapping pork filtering prawns mapping prawns filtering chicken mapping chicken [pork, prawns, chicken]
根据上例能够发现,尽管filter和map是两个独立的操做,但它们合并到同一次遍历中了(这种技术称为循环合并)。
二. 流与集合的异同
集合与流之间的差别在于何时进行计算。
集合:是一个内存中的数据结构,它包含数据结构中目前全部的值--集合中的每一个元素都得先算出来才能添加到集合中。
流:是在概念上固定的数据结构(不能添加或删除元素),其元素师按需计算的。
流就像是一个延迟建立的集合:只有在消费者要求的时候才会计算值。
1.流只能遍历一次
流和迭代器相似,只能遍历一次,遍历后说明这个流已经被消费了。
public static void main(String[] args) { List<String> title = Arrays.asList("dispatch", "settlement", "wyvern"); Stream<String> s = title.stream(); s.forEach(System.out::println); s.forEach(System.out::println); //当执行到这里时会报IllegalStateException: stream has already been operated upon or closed }
2.集合和流在遍历数据方式的区别
Collection接口须要作迭代(for-each),称为外部迭代。
Stream库使用内部迭代。
内部迭代的优势:项目能够透明地并行处理,或者用更优化的顺序进行处理。
内部迭代的前提:你已经预先定义好了可以隐藏迭代的操做列表,例如filter或map。