Java8 Stream

1 Java8 Stream作用

#为集合提供聚合操作的能力
这个SQL的操作就是聚合操作,在查出结果后进行最大值的计算:

SELECT max(salary), employee_id, employee_name FROM Employee; 

Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作或大批量数据操作,如:遍历、排序、过滤等;

2 Java8 Stream详解

2.1 Stream是什么
#Stream(流)是一个来自数据源的元素队列并支持聚合操作;
#与java.io包里的InputStream和OutputStream是完全不同的概念;
#不同于XML 解析的 Stream,也不是大数据实时处理的 Stream;

Stream 不是集合元素,它不是数据结构并不保存数据,它和算法、计算有关,它更像一个高级版本的 Iterator,像迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历完即用尽了,就好比流水从面前流过,一去不复返,和迭代器又不同的是,Stream 可以并行化操作,迭代器只能命令式地、串行化操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程;

1 串行遍历时,每个 item 读完后再读下一个 item;
2 并行遍历时,数据会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出;

2.2 java.util.stream包
《Java8 Stream》
由关系图可以看到Stream接口只有一个实现类:ReferencePipeline
重要的***filter、map***等方法就是在该类里实现的;

2.3 Stream执行步骤
当我们使用一个流的时候,通常包括三个基本步骤:
***获取一个数据源(source)***→ 数据转换***→***执行各种操作并获取结果
每次转换原有 Stream 对象不改变(可以有多次转换),返回一个新的 Stream 对象;
这就允许对其操作可以像链条一样排列,变成一个管道,如下图所示:
《Java8 Stream》

2.4 Stream操作类别
***Intermediate(中间操作)***:一个流可以后面跟随零个或多个 intermediate 操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。
***Terminal(结束操作)***:一个流只能有一个 terminal 操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作。Terminal 操作的执行,才会真正开始流的遍历(内部迭代),并且会生成一个结果,或者一个 side effect。

Intermediate(中间操作)通过管道模式(Pipeline)实现
Terminal(结束操作)通过访问者模式(Visitor)实现

2.5 Stream生成方式
《Java8 Stream》
Collection接口新增了default方法来获取stream,只要实现了Collection接口的都可以通过该方法获取;
《Java8 Stream》
Arrays类新增了好几个static方法来获取stream,Arrays可以通过这些方法获取;

总结下来,stream的获取方式如下:

## 1从 Collection 和数组
Collection.stream()
Collection.parallelStream()
Arrays.stream(T array) or Stream.of()
## 2从 BufferedReader
java.io.BufferedReader.lines()
## 3静态工厂
java.util.stream.IntStream.range()
java.nio.file.Files.walk()
## 4自己构建
java.util.Spliterator
## 5其它
Random.ints()
BitSet.stream()
Pattern.splitAsStream(java.lang.CharSequence)
JarFile.stream()

获取Stream:

 //获取流
 //1 Stream.of(val1, val2, val3….)
 System.out.println("-------------Stream.of(val1)-------------");
 Stream<Integer> newStream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9);
 newStream.forEach(System.out::println);
 //2 Stream.of(arrayOfElements)
 System.out.println("-------------Stream.of(arrayOfElements)-------------");
 Stream<Integer> newStream1 = Stream.of(new Integer[]{1, 2, 3, 4, 5, 6, 7, 8, 9});
 newStream1.forEach(System.out::println);
 //3 someList.stream()
 System.out.println("-------------someList.stream()-------------");
 List<Integer> list = new ArrayList<Integer>();
 for (int i = 1; i < 10; i++) {
  list.add(i);
  }
 Stream<Integer> newStream2 = list.stream();
 newStream2.forEach(System.out::println);
 //4 Stream.generate() or Stream.iterate()
 System.out.println("-------------Stream.generate()-------------");
 Stream<Date> newStream3 = Stream.generate(() -> {
   return new Date();
  });
 newStream3.forEach(System.out::println);

 //5 String chars or String tokens
 System.out.println("-------------String chars()-------------");
 IntStream newStream4 = "12345_abcdefg".chars();
 newStream4.forEach(System.out::println);
 // OR
 Stream<String> newStream5 = Stream.of("A$B$C".split("\\$"));
 newStream5.forEach(System.out::println);
3 Stream主要方法

《Java8 Stream》
3.1 Stream API
#1 流到流之间的转换
map(映射转换)
mapTo[Int|Long|Double] (到原子类型流的转换)
flatMap (把Stream 中的层级结构扁平化)
flatMapTo[Int|Long|Double](把Stream 中的层级结构扁平化成int、long或double)
sorted(排序)
distinct(不重复值)
peek(执行某种操作,流不变,可用于调试)
limit(限制到指定元素数量)
skip(跳过若干元素)
#2 流到终值的转换
toArray(转为数组)
collect(聚合结果)
min(最小值)
max(最大值)
count (元素个数)
anyMatch (任一匹配)
noneMatch(一个都不匹配)
findFirst(选择首元素)
findAny(任选一元素)
#3 直接遍历
forEach (不保序遍历,比如并行流)
forEachOrdered(保序遍历)
#4 构造流
empty (构造空流)
of (单个元素的流及多元素顺序流)
iterate (无限长度的有序顺序流)
generate (将数据提供器转换成无限非有序的顺序流)
concat (流的连接)
Builder (用于构造流的Builder对象)

3.2 方法使用案例
如果没有重新声明,案例中均使用这一个流:

//获取数组流
List<String> strings = Arrays.asList("555", "333", "444", "111", "222", "666")
//stream() 为strings创建串行流
Stream<String> stream = strings.stream();

注1:案例涉及Lambda表达式函数式接口方法引用,提前了解后再看本案例;

注2:因为同一个stream只能有一次结束操作且案例用的都是一个stream,所以会注掉部分结束操作,否则会报:IllegalStateException: stream has already been operated upon or closed;

#1 forEach 迭代流中的元素
void forEach(Consumer< ? super T> action)
根据传入的lamdba表达式进行消费

//1 forEach 迭代流中的元素
System.out.println("-------------forEach 遍历stream中的元素-------------");
strings.stream().forEach(System.out::println);

执行结果:

-------------forEach 遍历stream中的元素-------------
555
333
444
111
222
666

#2 filter 根据条件过滤流中元素
Stream filter(Predicate< ? super T> predicate)
根据传入的lamdba表达式对数据进行过滤

//2 filter 根据条件过滤流中元素
//2.1和2.2需分开执行,否则会报错:IllegalStateException: stream has already been operated upon or closed
System.out.println("-------------filter 过滤stream中符合条件的元素并返回Stream-------------");
//2.1 过滤获取等于aaa的元素,并打印集合
//stream.filter(element -> "666".equals(element)).forEach(System.out::println);
//2.2 过滤非空元素,并获取数组长度
long count = stream1.filter(element -> !element.isEmpty()).count();
System.out.println("stream中有[" + count + "]个非空数据!");

执行结果:

-------------filter 过滤stream中符合条件的元素并返回Stream-------------
666
##注掉2.1后执行2.2
-------------filter 过滤stream中符合条件的元素并返回Stream-------------
stream中有[6]个非空数据!

#3 map 映射转换流中元素
Stream map(Function< ? super T, ? extends R> mapper)
根据传入的lamdba表达式进行数据转换

//3 map 遍历每个元素的value,对其执行传入的lambda表达式后返回Stream
System.out.println("-------------map()-------------");
//完整的使用方法:strings.stream().map(i -> i + "@@!").forEach(System.out::println);
//拆分成两步:1 遍历元素value并执行传入的lambda表达式
Stream<String> stringStream = strings.stream().map(i -> i + "@@!");
//拆分成两步:2 遍历stream并打印元素
stringStream.forEach(System.out::println);

执行结果:

-------------map 遍历元素value并执行传入的lambda表达式-------------
555@@!
333@@!
444@@!
111@@!
222@@!
666@@!

#4 mapToInt 遍历每个元素的value,对其元素类型进行转换
IntStream mapToInt(ToIntFunction< ? super T> mapper)

//4 mapToInt(mapToInt/mapToLong/mapToDouble同) 遍历每个元素的value,对其元素类型进行转换
System.out.println("-------------mapToInt 遍历元素value并执行int转换lambda表达式-------------");
//完整的使用方法:strings.stream().mapToInt(element -> Integer.valueOf(element)).forEach(System.out::println);
//拆分成两步:1 遍历元素value并执行传入的类型转换lambda表达式
IntStream intStream = strings.stream().mapToInt(element -> Integer.valueOf(element));
//拆分成两步:2 遍历intStream并打印元素
intStream.forEach(System.out::println);

执行结果:

-------------mapToInt 遍历元素value并执行int转换lambda表达式-------------
555
333
444
111
222
666

#5 flatMap 把Stream中的层级结构扁平化并返回Stream
Stream flatMap(Function< ? super T, ? extends Stream< ? extends R>> mapper)

//5 flatMap 把Stream中的层级结构扁平化并返回Stream
//如List中存在List:
//在获取第一层stream后再将最底层元素抽出来放到一起,最终 output 的新 Stream 里面已经没有 List 了,都是直接的数字
//Stream可以容纳不同的数据类型,Stream操作(filter,sum,distinct ...)和collectors不支持它,所以我们需要使用flatMap()进行以下转换
System.out.println("-------------flatMap 转换集合内集合(其他对象)-------------");
String[][] arr = new String[][]{{"333", "444"}, {"111", "555"}, {"666", "222"}};
//打印
Arrays.stream(arr).forEach(System.out::println);
System.out.println("-------------flatMap 遍历元素value并执行传入的lambda表达式-------------");
Arrays.stream(arr).flatMap(x -> Arrays.stream(x)).forEach(System.out::println);
//聚合操作 filter
System.out.println("-------------二维数组未使用flatMap过滤等于666的元素-------------");
Arrays.stream(arr).filter(element -> "666".equals(element)).forEach(System.out::println);
System.out.println("-------------二维数组使用flatMap过滤等于666的元素-------------");
Arrays.stream(arr).flatMap(x -> Arrays.stream(x)).filter(element -> "666".equals(element)).forEach(System.out::println);


执行结果:

-------------flatMap 转换集合内集合(其他对象)-------------
[Ljava.lang.String;@6f496d9f
[Ljava.lang.String;@723279cf
[Ljava.lang.String;@10f87f48
-------------flatMap 遍历元素value并执行传入的lambda表达式-------------
333
444
111
555
666
222
-------------二维数组未使用flatMap过滤等于666的元素-------------
-------------二维数组使用flatMap过滤等于666的元素-------------
666

#6 flatMapToInt 把Stream中的层级结构扁平化并返回Stream
IntStream flatMapToInt(Function< ? super T, ? extends IntStream> mapper)

//6 flatMapToInt(flatMapToInt/flatMapToLong/flatMapToDouble) 把Stream中的层级结构扁平化并返回Stream
System.out.println("-------------flatMapToInt 遍历元素value,执行传入的lambda表达式-------------");
//6.1 将数组元素转换成int型
List<String> stringList = Arrays.asList("1", "2", "3", "4", "5");
stringList.stream().flatMapToInt(num -> IntStream.of(Integer.parseInt(num))).forEach(System.out::println);
//6.2 获取元素长度
System.out.println("-------------flatMapToInt 遍历元素value,执行传入的lambda表达式-------------");
List<String> stringList1 = Arrays.asList("Geeks", "GFG", "GeeksforGeeks", "gfg");
stringList1.stream().flatMapToInt(str -> IntStream.of(str.length())).forEach(System.out::println);

执行结果:

-------------flatMapToInt 遍历元素value,执行传入的lambda表达式-------------
1
2
3
4
5
-------------flatMapToInt 遍历元素value,执行传入的lambda表达式-------------
5
3
13
3

#7 distinct 遍历每个元素的value,对其进行去重后返回Stream
Stream distinct()

System.out.println("-------------distinct 遍历元素value并去重-------------");
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
numbers.stream().distinct().forEach(System.out::println);

执行结果:

-------------distinct 遍历元素value并去重-------------
3
2
7
5

#8 skip 跳过n个元素后返回Stream
Stream skip(long n)

//8 skip 跳过n个元素后返回Stream
System.out.println("-------------skip 跳过几个元素-------------");
strings.stream().skip(3).forEach(System.out::println);

执行结果:

-------------skip 跳过3个元素-------------
111
222
666

#9 peek 生成一个带有作用于每个元素的消费型lambda表达式的Stream,在Stream元素被消费时,该lambda表达式会被执行
Stream peek(Consumer< ? super T> action)

//9 peek 生成一个带有作用于每个元素的消费型lambda表达式的Stream,在Stream元素被消费时,该lambda表达式会被执行
System.out.println("-------------peek 元素消费时执行传入的lambda表达式-------------");
strings.stream().peek(element -> System.out.println("元素被消费时执行-->" + element)).forEach(System.out::println);

执行结果:

-------------peek 元素消费时执行传入的lambda表达式-------------
元素被消费时执行-->555
555
元素被消费时执行-->333
333
元素被消费时执行-->444
444
元素被消费时执行-->111
111
元素被消费时执行-->222
222
元素被消费时执行-->666
666

#10 limit 获取指定数量的流
Stream limit(long maxSize)

//10 limit 获取指定数量的流
System.out.println("-------------limit 获取4个元素的流-------------");
strings.stream().limit(4).forEach(System.out::println);

执行结果:

-------------limit 获取4个元素的流-------------
555
333
444
111

#11 sorted 对流元素进行排序
Stream sorted()

//11 sorted 对流元素进行排序
System.out.println("-------------sorted 对流元素进行排序-------------");
strings.stream().sorted().forEach(System.out::println);

执行结果:

-------------sorted 对流元素进行排序-------------
111
222
333
444
555
666

#12 reduce 对流元素进行组合
T reduce(T identity, BinaryOperator accumulator)
它提供一个起始值(种子),然后依照运算规则(BinaryOperator),和前面 Stream 的第一个、第二个、第 n 个元素组合
从这个意义上说,字符串拼接、数值的 sum、min、max、average 都是特殊的 reduce

//12 reduce 把Stream元素组合起来,组合规则为传入的lambda表达式
//它提供一个起始值(种子),然后依照运算规则(BinaryOperator),和前面 Stream 的第一个、第二个、第 n 个元素组合
//从这个意义上说,字符串拼接、数值的 sum、min、max、average 都是特殊的 reduce
System.out.println("-------------reduce 把Stream元素组合起来-------------");
// 字符串连接,concat = "ABCD"
String str = Stream.of("A", "B", "C", "D").reduce("", String::concat);
System.out.println("拼接字符串:" + str);
// 求最小值,minValue = -3.0
double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min);
System.out.println("最小值:" + minValue);
// 求和,sumValue = 20, 有起始值
int sumValue = Stream.of(1, 2, 3, 4).reduce(10, Integer::sum);
System.out.println("求和有起始值:" + sumValue);
// 求和,sumValue = 10, 无起始值
sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
System.out.println("求和无起始值:" + sumValue);

执行结果:

-------------reduce 把Stream元素组合起来-------------
拼接字符串:ABCD
最小值:-3.0
求和有起始值:20
求和无起始值:10

#13 collect 归约操作,流中的元素累积成一个汇总结果
<R, A> R collect(Collector< ? super T, A, R> collector)
体的做法是通过定义新的Collector接口来定义的

//13 collect 归约操作,流中的元素累积成一个汇总结果
//具体的做法是通过定义新的Collector接口来定义的
System.out.println("-------------collect 汇总结果-------------");
//13.1 toList toSet toMap
Stream<String> stream1 = Arrays.asList("555", "333", "444", "111", "222", "666").stream();
List<String> list = stream1.filter(element -> !"666".equals(element)).collect(Collectors.toList());
System.out.println("汇总结果list-->" + list);
Stream<String> stream2 = Arrays.asList("555", "333", "444", "111", "222", "666").stream();
Set<String> set = stream2.filter(element -> !"666".equals(element)).collect(Collectors.toSet());
System.out.println("汇总结果set-->" + set);
Stream<String> stream3 = Arrays.asList("555", "333", "444", "111", "222", "666").stream();
Map<String, String> map = stream3.filter(element -> !"666".equals(element)).collect(Collectors.toMap(x -> x, x -> x + "1"));
System.out.println("汇总结果map-->" + map);
//13.2 joining counting
Stream<String> stream4 = Arrays.asList("555", "333", "444", "111", "222", "666").stream();
String joining = stream4.map(p -> p).collect(Collectors.joining(","));
System.out.println("collect joining-->" + joining);
Stream<String> stream5 = Arrays.asList("555", "333", "444", "111", "222", "666").stream();
Long counting = stream5.map(p -> p).collect(Collectors.counting());
System.out.println("collect counting-->" + counting);
//13.3 maxBy groupingBy
List<Integer> integers = Arrays.asList(555, 333, 444, 111, 222, 666);
Stream<Integer> stream6 = integers.stream();
Integer integer = stream6.map(p -> p).collect(Collectors.maxBy(Comparator.comparingInt(o -> o))).get();
System.out.println("collect integer-->" + integer);

执行结果:

-------------collect 汇总结果-------------
汇总结果list-->[555, 333, 444, 111, 222]
汇总结果set-->[111, 222, 333, 444, 555]
汇总结果map-->{111=1111, 222=2221, 333=3331, 444=4441, 555=5551}
collect joining-->555,333,444,111,222,666
collect counting-->6
collect integer-->666

涉及代码:–>GitHub

参考文献:
[ 1 ] https://howtodoinjava.com/java8/java-8-tutorial-streams-by-examples/
[ 2 ] https://www.tutorialspoint.com/java8/java8_streams.htm
[ 3 ] https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/
[ 4 ] https://java2blog.com/java-8-tutorial/
[ 5 ] https://www.jianshu.com/p/ccbb42ad9551

    原文作者:Weison Wei
    原文地址: https://blog.csdn.net/weixx3/article/details/81211620
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞