java8新特性:三,Stream
1 Stream介绍
Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
±-------------------+ ±-----+ ±-----+ ±–+ ±------+
| stream of elements ±----> |filter±> |sorted±> |map±> |collect|
±-------------------+ ±-----+ ±-----+ ±–+ ±------+
以上的流程转换为 Java 代码为:
List<Integer> transactionsIds =
widgets.stream()
.filter(b -> b.getColor() == RED)
.sorted((x,y) -> x.getWeight() - y.getWeight())
.mapToInt(Widget::getWeight)
.sum();
2 什么是 Stream?
Stream(流)是一个来自数据源的元素队列并支持聚合操作
元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。
和以前的Collection操作不同, Stream操作还有两个基础的特征:
Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。
Stream可以由数组或集合创建,对流的操作分为两种:
1.中间操作:每次返回一个新的流,可以有多个。
2.终端操作:每个流只能进行一次终端操作,终端操作结束后流无法再次使用。终端操作会产生一个新的集合或值。
Stream有以下几个特性:
1.stream不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果。
2.stream不会改变数据源,通常情况下会产生一个新的集合或一个值。
3.stream具有延迟执行特性,只有调用终端操作时,中间操作才会执行。
3.生成流
在 Java 8 中, 集合接口有两个方法来生成流:
stream() − 为集合创建串行流。
parallelStream() − 为集合创建并行流。
tips:java 字符串为空的情况
String a="";
String b=null;
System.out.println(a.isEmpty());
System.out.println(b.isEmpty());
生成流的例子:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class UseStream {
public static void main(String[] args) {
List<String> s= Arrays.asList("python"," ","xiaoxu","123","js","");
System.out.println(s);
System.out.println(s.getClass());
System.out.println(s.stream().filter(str -> !str.isEmpty()));
System.out.println(s.stream().filter(str -> !str.isEmpty()).collect(Collectors.toList()));
System.out.println(s.stream().filter(str -> !str.isEmpty()).collect(Collectors.toList()).getClass());
List<String> filtered=s.stream().filter(str -> !str.isEmpty()).collect(Collectors.toList());
System.out.println(filtered);
System.out.println(filtered.getClass());
}
}
创建多种方式如下,stream可以通过集合数组创建:
1.通过java.util.Collection.stream(),java.util.Collection.parallelStream()用集合创建流
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class Mstream {
public static void main(String[] args) {
//1.通过java.util.Collection.stream(),java.util.Collection.parallelStream()创建
List<String> s= Arrays.asList("a","b");
//创建一个顺序流
Stream<String> stream=s.stream();
//创建一个并行流
Stream<String> parallelStream=s.parallelStream();
}
}
2.通过java.util.Arrays.stream(T[] array)方法用数组创建流:
import java.util.stream.IntStream;
import java.util.Arrays;
int[] arr={1,5,7};
IntStream ints_tream=Arrays.stream(arr);
3.使用Stream的静态方法:of()、iterate()、generate()
import java.util.stream.Stream;
Stream<Integer> i1=Stream.of(1,8,9);
Stream<Integer> i2=Stream.iterate(5,x->x+3).limit(4);
i2.forEach(System.out::println);
Stream<Double> i3=Stream.generate(Math::random).limit(3);
i3.forEach(System.out::println);
5
8
11
14
0.02766533296035878
0.779837131876051
0.6171275733047784
4.stream和parallelStream简单区分:stream是顺序流,由主线程按顺序对流执行操作,而parallelStream是并行流,内部以多线程并行执行的方式对流进行操作,前提是流中的数据处理没有顺序要求。如果流中的数据量足够大,并行流可以加快处理度。
除了直接创建并行流,还可以通过parallel()把顺序流转换成并行流:
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
List<Integer> ll=Arrays.asList(1,4,4,6);
Optional<Integer> k=ll.stream().parallel().filter(x->x==4).findFirst();
System.out.println(k.get());
4
5.Optional
Optional类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
4 forEach
Stream 提供了新的方法 'forEach’来迭代流中的每个数据。
java中有多种方式对集合进行遍历,Collection.stream().forEach()和Collection.forEach()
4.1 增强for遍历集合
idea快捷导入类(import class),比如下面,在List后面光标,按下alt+enter,按下"import class",就会导入:import java.util.List
常见的java遍历集合的形式:增强for,输入a.for,idea快捷形成如下快捷方式:
4.2 函数式java:Collection.forEach()遍历集合
使用函数式JavaCollection.forEach()遍历:
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
public class ForEac {
public static void main(String[] args) {
List<String> a= Arrays.asList("A","D","C","E");
Consumer<String> consumer_me= System.out::println;
a.forEach(consumer_me);
}
}
4.3 集合流上调用:Collection.stream().forEach()
public static void main(String[] args) {
List<String> a= Arrays.asList("A","D","C","E");
Consumer<String> consumer_me= System.out::println;
a.forEach(consumer_me);
a.stream().forEach(consumer_me);
}
4.4 Parallel Stream
并发流允许我们在多个线程中执行stream,在这种情况下,执行顺序也不明确的。Java只需要在调用任何最终操作(例如Collectors.toList() )之前完成所有线程。
public class ForEac {
public static void main(String[] args) {
List<String> a= Arrays.asList("A","D","C","E");
Consumer<String> consumer_me= System.out::println;
a.forEach(consumer_me);
// a.stream().forEach(consumer_me);
System.out.println("-----------------");
a.parallelStream().forEach(System.out::println);
}
}
并发流,并不会按照插入顺序打印:
4.5 列表中使用的forEach()会使用自定义迭代器,而stream().forEach()只是从列表中逐个获取元素,会忽略迭代器
4.6 两个forEach()方法之间的另一个细微差别是Java明确允许使用迭代器修改元素。相反,stream不能。
4.7 例子:以下代码片段使用 forEach 输出了10个随机数
public static void main(String[] args) {
Random random=new Random();
System.out.println(random.ints());
System.out.println(random.ints().limit(10));
random.ints().limit(10).forEach(System.out::println);
}
5 map
map 方法用于映射每个元素到对应的结果,以下代码片段使用 map 输出了元素对应的平方数
public static void main(String[] args) {
List<Integer> num= Arrays.asList(1,3,6,8,9,1);
List<Integer> squareList=num.stream().map(i->i*i).distinct().collect(Collectors.toList());
System.out.println(squareList);
}
[1, 9, 36, 64, 81]
6 filter
filter 方法用于通过设置的条件过滤出元素。以下代码片段使用 filter 方法过滤出空字符串
List<String> str=Arrays.asList("abc","","gh"," ","e");
List<String> fil_str=str.stream().filter(s->!s.isEmpty()).collect(Collectors.toList());
System.out.println(fil_str);
[abc, gh, , e]
tips:因为字符串的isEmpty(),对于null来调用,会报空指针异常
所以在使用stream()的时候,要注意lambda方法中,异常数据是否会抛错的情况
7 limit
limit 方法用于获取指定数量的流。 以下代码片段使用 limit 方法打印出 10 条数据
Random r=new Random();
r.ints().limit(10).forEach(System.out::println);
8 sorted
sorted 方法用于对流进行排序。以下代码片段使用 sorted 方法对输出的 10 个随机数进行排序
Random r=new Random();
r.ints().limit(10).sorted().forEach(System.out::println);
9 Collectors
Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors 可用于返回列表或字符串
List<String> s= Arrays.asList("abc", "", "bc", "efg", "abcd"," ", "jkl");
List<String> fil=s.stream().filter(str->!str.isEmpty()).collect(Collectors.toList());
System.out.println("筛选列表:"+fil);
String st=s.stream().filter(str->!str.isEmpty()).collect(Collectors.joining(";"));
System.out.println("合并字符串:"+st);
筛选列表:[abc, bc, efg, abcd, , jkl]
合并字符串:abc;bc;efg;abcd; ;jkl
10 统计
另外,一些产生统计结果的收集器也非常有用。它们主要用于int、double、long等基本类型上,它们可以用来产生类似如下的统计结果。
import java.util.*;
public static void main(String[] args) {
List<Integer> numbers=Arrays.asList(3,2,2,3,7,9,3);
IntSummaryStatistics stats=numbers.stream().mapToInt(x->x).summaryStatistics();
System.out.println("列表中最大的数:"+stats.getMax());
System.out.println("列表中最小的数:"+stats.getMin());
System.out.println("所有数之和:"+stats.getSum());
System.out.println("平均数:"+stats.getAverage());
}