一、初识流
1. 什么是流?
流是Java API的新成员,它允许你以声明性方式(类似SQL,只写做什么,不考虑怎么做)处理数据集合。流就是从支持数据处理操作的源生成的元素序列。源可以是集合、数组或IO资源,数据处理操作包括filter(过滤),map(映射),reduce(累积操作),find(查找),match(匹配),sort(排序)。流可以顺序执行,也可以并行执行。
2. 集合与流
集合是内存中的数据结构,静态地包含所有值。流是从数据源中不断读取数据的固定数据结构,动态地获取值。
和迭代器类似,流只能遍历一次。遍历完之后,流就被消费了。
3. 内部迭代与外部迭代
外部迭代是指用户手动写循环进行的迭代(如while,foreach),内部迭代是API内部提供的迭代,Stream库中内置了迭代,称为内部迭代。
4. 中间操作与终端操作
只有一个终端操作,终端操作之后就结束了,终端操作前面的操作都是中间操作。
二、使用流
1. 筛选、切片和映射
筛选:
List<Integer> ints = Arrays.asList(1, 3, 2, 4, 5, 6, 9, 8, 7);
ints.stream()
.filter(i-> i%2==0)//过滤出偶数
.forEach(System.out::println);
切片:
Java 9引入两个新方法,takeWhile和dropWhile。
假设一个数组已经排好序,现在要找出小于等于10的数字,如果使用filter,就会遍历整个数组,事实上,遇到第一个大于10的数就应该停止,这时候使用takeWhile,用法与filter一样。
dropWhile是takeWhile的补充,遍历时,遇到为false的元素丢弃,当遇到第一个为true的元素就返回。
映射:
//Student类有id,name,age属性,现有List<Student> list
List<String> names= list.stream
.map(Student::getName)
.collect(toList());
将Student映射为name。
流的扁平化flatMap:设有字符串数组["Hello","World"],现在要返回不含重复字符的字符数组[H,e,l,o,W,r,d]
这样写只能对单词和单词之间进行去重。
使用map将每个单词切分为一个个字符,flatMap将多个流合并为一个流,然后去重。
2. 查询和匹配
allMatch:所有元素都匹配
anyMatch:至少匹配一个元素
noneMatch:全都不匹配返回true
findFirst:找到第一个符合的
findAny:找到任意一个符合的,效率比findFirst快。
3. 归约reduce
元素求和:
int sum=numbers.stream().reduce(0,(a,b)->a+b);
reduce的第一个参数是初始值,后面是方法参数和方法体
有状态的流和无状态的流
有无状态,主要看单次操作是否与之前操作有联系。
map、filter操作都是独立的,它们是无状态的流。
reduce,sum,max,sort,distinct等都需要保存之前的结果,并与当前结果对比,它们是有状态的流。
4. 数值流和对象流
mapToInt返回的是IntStream流(不同于Stream<Integer>),还有mapToLong,mapToDouble等,一方面数值流可以避免空指针问题,另一方面避免拆箱装箱造成的性能损失。
数值流可以转换回对象流,intStream.boxed()
数值范围 IntStream.rangeClosed(1,100) //从1到100
5. 构建流
(1)由值创建流
Stream<String> stream=Stream.of("a","b","cd","Action");
空流Stream<String> stream=Stream.empty();
(2) 由可空对象创建流
Stream<String> home=Stream.ofNullable(System.getProperty("home"))
(3) 由数组创建流
Arrays.stream(arr)
(4) 由文件创建流
Files.lines会返回一个由指定文件中的各行构成的字符串流。
(5) 由函数生成流 :创建无限流
1)迭代
创建了0,2,4,6,8,10......18共10个偶数。
如果没有加上limit,会一直创建下去。
2)生成
生成了5个随机数。
参考:《Java实战》第2版