- 1. 引言
- 2. 流的生成 Stream Source
- 3. 流的使用
- 3.1 使用流的一般步骤
- 3.2 Stream 与其它数据结构的转换
- 3.3 Stream 的操作
- 3.3.1 通过数组创建Stream
- 3.3.2 count 统计列表中某元素出现的个数
- 3.3.3 filter 列表对筛选元素进行操作并转化为集合存储
- 3.3.4 sum 列表求和(reduce 好在可以设定初值, collector.summarize 优势在输出结果非常丰富)
- 3.3.5 average 列表求平均数
- 3.3.6 max 求元素的最大数
- 3.3.7 connect 字符串拼接
- 3.3.8 groupby 分组
- 3.3.9 map 根据 list 创建 map
- 3.3.10 flapMap
- 3.3.12 重用 a stream chain 的中间操作
- 3.3.13 limit、skip 等
- 3.3.14 排序
- 3.3.15 distinct
- 3.3.16 Match
- 3.4 返回值 optional
- 参考文献
1. 引言
1.1 应用
- 对集合(Collection)对象功能的增强,提供串行和并行两种模式进行聚合操作(aggregate operation);
- 借助 Lambda 进行大批量数据操作(bulk data operation) ;
补充:
Stream 非集合元素,非数据结构,用操作管道从 source(Collection、数组、generator function、IO channel)抓取数据且数据量不限;
- 所有 Stream 的操作必须以 lambda 表达式为参数;
- 不支持索引访问;
- 容易实现向其他数据结构,如 List、Set、Array 等的转换;
- 高级版本的 Iterator:
- 单向,不可往复,数据只能遍历一次;
- Iterator 只能显示地通过命令式执行串行化操作,而 Stream 可并行化地隐式执行数据转换和计算;
-
2. 流的生成 Stream Source
2.1 方式
Collection 和 数组
- Collection.stream()
- Collection.parallelStream()
- Arrays.stream(T array)
- BufferedReader
- java.io.BufferedReader.lines()
通过 Stream 接口的静态工厂方法
- java.util.stream.IntStream.range()
- java.nio.file.Files.walk()
- Stream.of() : 有两个 overload 方法,一个接受变长参数,一个接口单一值
- Stream.generate(Math::random) : 生成一个无限长度的Stream,其元素的生成是通过给定的Supplier(这个接口可以看成一个对象的工厂,每次调用返回一个给定类型的对象);
- Stream.iterate(1, item -> item + 1).limit(10).forEach(System.out::println) : 生成无限长度的Stream,和generator 不同的是,其元素的生成是重复对给定的种子值(seed)调用用户指定函数来生成的。其中包含的元素可以认为是:seed,f(seed),f(f(seed))无限循环;
自定义构建 java.util.Spliterator
其它
为三种基本数值型提供了对应的 Stream:IntStream、LongStream、DoubleStream;
补充:也可用 Stream、Stream >、Stream ,但 boxing 和 unboxing 会很耗时; 3. 流的使用
3.1 使用流的一般步骤
获取一个数据源(source)→ 数据转换 → 执行操作获取目标结果;
- 补充:每次转换原有 Stream 对象不改变,返回一个新的 Stream ;
3.2 Stream 与其它数据结构的转换
- Array
String[] strArray1 = stream.toArray(String[]::new); - Collection
Listlist1 = stream.collect(Collectors.toList());
Listlist2 = stream.collect(Collectors.toCollection(ArrayList::new));
Set set1 = stream.collect(Collectors.toSet());
Stack stack1 = stream.collect(Collectors.toCollection(Stack::new)); String
String str = stream.collect(Collectors.joining()).toString();3.3 Stream 的操作
流的操作类型
- Intermediate:一个流后可有零个或多个 intermediate 操作;
- 目的:打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用;
- 具有惰性化(lazy),仅实现方法调用,并未真正开始流的遍历;
- 包括方法:map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered;
- 补充:flatMap 和 map 类似,不同的是其每个元素转换得到的是 Stream 对象,会把子 Stream 中的元素压缩到父集合中;
- Terminal:一个流只能有一个 terminal 操作,为流的最后一个操作;
- 真正开始流的遍历,并且会生成一个结果(eg:count方法会有一个统计结果),或者一个 side effect(eg:forEach);
- 包括方法:forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator
- short-circuiting:
- 对于一个 intermediate 操作,若接受的是一个无限大(infinite/unbounded)的 Stream,可返回一个有限的新 Stream;
- 对于一个 terminal 操作,若接受的是一个无限大的 Stream,可在有限时间计算出结果;
- 当操作一个无限大的 Stream 且需在有限时间内完成操作,则在管道内拥有一个 short-circuiting 操作是必要非充分条件;
- 包括方法:anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit
- 补充: 一个 Stream 的多次转换操作 (Intermediate 操作) 只会在 Terminal 操作的时候融合起来,一次循环完成,可理解为 Stream 里有个操作函数的集合,每次转换操作就是把转换函数放入这个集合中,在 Terminal 操作的时候循环 Stream 对应的集合,然后对每个元素执行所有的函数。
- Intermediate:一个流后可有零个或多个 intermediate 操作;
- 操作实现
3.3.1 通过数组创建Stream
String[] arr_v = {"aa","bbb","cccc","dd"};
//方式一:
Stream<String> stringStream = Arrays.stream(arr_v);// 获取指定范围的 stream : Arrays.stream(arr_v, 1,3)
//也有针对具体元素类型的 InStream 与 DoubleStream
System.out.println(stringStream.count());// Output:4,统计数组的个数
//方式二:
Stream<String> stringStream01 = Stream.of(arr_v);
3.3.2 count 统计列表中某元素出现的个数
ArrayList<Integer> list_stream = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5,6,6,6));
list_stream.stream().filter(s->s.equals(6)).count();//outpput:3
3.3.3 filter 列表对筛选元素进行操作并转化为集合存储
list_stream.stream().filter(s->!s.equals(6)).map(s->s*s).collect(Collectors.toList())
//output:[1, 4, 9, 16, 25]
3.3.4 sum 列表求和(reduce 好在可以设定初值, collector.summarize 优势在输出结果非常丰富)
ArrayList<Integer> list_stream = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5,6,6,6));
list_stream.stream().reduce(2,(a,b)->a+2*b);// [Output : 68]初始 sum = 2,对每个元素乘以2再求和
list_stream.stream().collect(Collectors.summarizingInt(a->2*a));// IntSummaryStatistics{count=8, sum=66, min=2, average=8.250000, max=12}
3.3.5 average 列表求平均数
list_stream.stream().collect(Collectors.averagingInt(i->i))
3.3.6 max 求元素的最大数
List<Double> list_max = new Random().doubles(1,10).limit(5).boxed().collect(Collectors.toList());// 生成 【1,10】的 5 个随机数
//方式一:
list_max.stream().collect(Collectors.maxBy(Comparator.comparingDouble(i->i*3)));
//方式二:
list_max.stream().reduce(Math::max);
3.3.7 connect 字符串拼接
ArrayList<Integer> list_stream = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5,6,6,6));
list_stream.stream().map(i->i.toString()).collect(Collectors.joining("#"));// 1#2#3#4#5#6#6#6
list_stream.stream().map(i->i.toString()).collect(Collectors.joining(",","[","]"));//[1,2,3,4,5,6,6,6]
list_stream.stream().map(i->i.toString()).reduce("#", String::concat);//#12345666
list_stream.stream().map(Objects::toString).reduce("Start", String::concat);//Start12345666
list_stream.stream().map(Objects::toString).reduce(new StringJoiner(",","[","]"),StringJoiner::add,StringJoiner::merge));//[1,2,3,4,5,6,6,6]
3.3.8 groupby 分组
ArrayList<Integer> list_stream = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5,6,6,6));
System.out.println(list_stream.stream().collect(groupingBy(i->i>3)));
//{false=[1, 2, 3], true=[4, 5, 6, 6, 6]}
3.3.9 map 根据 list 创建 map
//获取 2-10 范围内数字的平方
List<Integer> list1 = IntStream.range(2,10).boxed().collect(Collectors.toList());
Map<Integer, Integer> map1 = list1.stream().collect(Collectors.toMap(p->p,q->q*2));// {2=4, 3=6, 4=8, 5=10, 6=12, 7=14, 8=16, 9=18}
//统计每个字符串元素的长度
System.out.println(Stream.of("cyt","ly","hkp","lrx","zjn").map(String::length).collect(Collectors.toList()));
3.3.10 flapMap
```java // 案例一: Map> map = new LinkedHashMap<>(); map.put(“a”, Arrays.asList(1,2,3)); map.put(“b”, Arrays.asList(4,5,6)); System.out.println(map.values().stream().flatMap(List::stream).collect(Collectors.toList())); // output: [1, 2, 3, 4, 5, 6]
// 案例二: List
<a name="eXSwt"></a>
#### 3.3.11 peak
```java
Stream.of("one","two","three","four").filter(v->v.length()>3).peek(v->System.out.println("the filtered value: "+v)).
map(String::toUpperCase).peek(v->System.out.println("the mappped value: "+v)).collect(Collectors.toList());
//output: the filtered value: three, the mappped value: THREE, the filtered value: four, the mappped value: FOUR
3.3.12 重用 a stream chain 的中间操作
Supplier<Stream<String>> streamSupplier = ()->Stream.of("cyt","ly","hkp","lrx","zjn").map(String::toUpperCase).sorted();
streamSupplier.get().filter(s->s.startsWith("C")).forEach(System.out::println); // output: CYT
System.out.println(streamSupplier.get().collect(Collectors.joining(",","*","*")));// output : *CYT,HKP,LRX,LY,ZJN*
3.3.13 limit、skip 等
limit 返回 stream 的前 n 个元素, skip 扔掉前 n 个元素
ArrayList<Integer> list_stream = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5,6,6,6)); System.out.println(list_stream.stream().limit(5).skip(2).collect(Collectors.toList()));//output: [3,4,5]
3.3.14 排序
Stream 的排序比数组排序强大之处在于可以对 stream 先进行各类 map、filter、limit、skip 等
Stream.of("cyt","ly","hkp","lrx","zjn").limit(4).sorted((p1,p2)->p1.compareTo(p2)).forEach(System.out::println);// output: cyt、hkp、lrx、ly
3.3.15 distinct
ArrayList<Integer> list_stream = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5,6,6,6)); System.out.println(list_stream.stream().distinct().collect(Collectors.toList())); // output : [1, 2, 3, 4, 5, 6]
3.3.16 Match
ArrayList<Integer> list_stream = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5,6,6,6)); System.out.println(list_stream.stream().allMatch(p->p < 6));//output: false System.out.println(list_stream.stream().anyMatch(p->p == 1));//output: true System.out.println(list_stream.stream().noneMatch(p->p == 0));//output: true
3.4 返回值 optional
public static void testOptional(){ // Stream 中的 findAny、max/min、reduce 等方法返回的均是 optional 值 String str1 = "cyt"; String str2=null; Optional.ofNullable(str1).ifPresent(System.out::println); Optional.ofNullable(str2).ifPresent(System.out::println);// optional 避免空指针异常 Stream.of(1,2,3).reduce(Integer::sum).ifPresent(System.out::println);//output: 6 System.out.println(Stream.of("a","b","c","d").reduce("*",String::concat)); System.out.println(Optional.ofNullable(str1).map(String::length).orElse(-1));//output: 3 System.out.println(Optional.ofNullable(str2).map(String::length).orElse(-1));//output: -1 ArrayList<Integer> list_stream = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5,6,6,6)); System.out.println(list_stream.stream().allMatch(p->p < 6)); System.out.println(list_stream.stream().noneMatch(p->p == 0));//output: true }