Stream 是Java SE 8类库中新增的关键抽象,它被定义于 java.util.stream (这个包里有若干流类型: Stream 代表对象引用流,此外还有一系列特化流,如 IntStream,LongStream,DoubleStream等 ),Java 8 引入的的Stream主要用于取代部分Collection的操作,每个流代表一个值序列,流提供一系列常用的聚集操作,可以便捷的在它上面进行各种运算。集合类库也提供了便捷的方式使我们可以以操作流的方式使用集合、数组以及其它数据结构;
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
stream 相对于 Collection 的优点
1、无存储:流并不存储值;流的元素源自数据源(可能是某个数据结构、生成函数或I/O通道等等),通过一系列计算步骤得到;
2、函数式风格:对流的操作会产生一个结果,但流的数据源不会被修改;
3、惰性求值:多数流操作(包括过滤、映射、排序以及去重)都可以以惰性方式实现。这使得我们可以用一遍遍历完成整个流水线操作,并可以用短路操作提供更高效的实现;
4、无需上界:不少问题都可以被表达为无限流(infinite stream):用户不停地读取流直到满意的结果出现为止(比如说,枚举 完美数 这个操作可以被表达为在所有整数上进行过滤);集合是有限的,但流可以表达为无线流;
5、代码简练:对于一些collection的迭代处理操作,使用 stream 编写可以十分简洁,如果使用传统的 collection 迭代操作,代码可能十分啰嗦,可读性也会比较糟糕;
1、集合接口有两个方法来生成流:
stream() − 为集合创建串行流。
parallelStream() − 为集合创建并行流。parallelStream默认的并行线程数为cpu核数。
public static void main(String[] args) {
/**
* 定义集合l1 并为集合创建串行流
*/
List l1 = Arrays.asList("周星驰", "周杰伦", "周星星", "周润发");
// 返回串行流
l1.stream();
// 返回并行流
l1.parallelStream();
}
2、值创建流
Stream.of(T…) : Stream.of(“aa”, “bb”) 生成流
//值创建流 生成一个字符串流
Stream stream = Stream.of("java8", "Spring", "SpringCloud");
stream.forEach(System.out::println);
3、数组创建流
根据参数的数组类型创建对应的流。
Arrays.stream(T[ ])
Arrays.stream(int[ ])
Arrays.stream(double[ ])
Arrays.stream(long[ ])
// 只取索引第 1 到第 2 位的:
int[] a = {1, 2, 3, 4};
Arrays.stream(a, 1, 3).forEach(System.out :: println);
4、文件生成流
//每个元素是给定文件的其中一行
Stream stream02 = Files.lines(Paths.get("data.txt"));
5、函数生成流
两个方法:
iterate : 依次对每个新生成的值应用函数
generate :接受一个函数,生成一个新的值
/生成流,首元素为 0,之后依次加 2
Stream.iterate(0, n -> n + 2)
//生成流,为 0 到 1 的随机双精度数
Stream.generate(Math :: random)
//生成流,元素全为 1
Stream.generate(() -> 1)
中间操作
当数据源中的数据上了流水线后,这个过程对数据进行的所有操作都称为“中间操作”;
中间操作仍然会返回一个流对象,因此多个中间操作可以串连起来形成一个流水线;
stream 提供了多种类型的中间操作如下:
1、筛选与切片
filter——接收 Lambda , 从流中排除某些元素。
limit——截断流,使其元素不超过给定数量。
skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
distinct——筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
2、映射
map——接收 Lambda , 将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap——接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
3、排序
sorted()——自然排序(Comparable)
sorted(Comparator com)——定制排序
终止操作
allMatch——检查是否匹配所有元素
anyMatch——检查是否至少匹配一个元素
noneMatch——检查是否没有匹配的元素
findFirst——返回第一个元素
findAny——返回当前流中的任意元素
count——返回流中元素的总个数
max——返回流中最大值
min——返回流中最小值
reduce(T identity, BinaryOperator) / reduce(BinaryOperator) ——归约:可以将流中元素反复结合起来,得到一个值。
collect——收集:将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法,转换list,set等,参数为Collectors中的 toList,toSet,toCollection,toMap。
常用操作
1、两个集合匹配并赋值
list3= list1.stream()
.map(e -> list2.stream()
.filter(e1 -> e.name().equals(e1.name()))
.findFirst()
.map(e1 -> {
e.setSex(e1.getSex());
e.setAge(e1.getAex());
return e;
}).orElse(null))
.collect(Collectors.toList());
Optional类是Java8为了解决null值判断问题,借鉴google guava类库的Optional类而引入的一个同名Optional类,使用Optional类可以避免显式的null值判断(null的防御性检查),避免null导致的NPE(NullPointerException)。Optional 类的引入很好的解决空指针异常。
Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
看如下代码:
String[] strs = {"1","2"};
if(strs == null){
System.out.println("0");
}else{
System.out.println(strs.length);
}
这是打印strs数组长度的实现。
如用optional实现如下:
String[] strs = {"1","2"};
System.out.println(Optional.ofNullable(strs).map(str->str.length).orElse(0));
可以看到用Optional的方式更加优雅。
我们看下Optional类的部分源码:
private static final Optional> EMPTY = new Optional<>();
private final T value;
private Optional() {
this.value = null;
}
public static Optional empty() {
@SuppressWarnings("unchecked")
Optional t = (Optional) EMPTY;
return t;
}
private Optional(T value) {
this.value = Objects.requireNonNull(value);
}
public static Optional of(T value) {
return new Optional<>(value);
}
public static Optional ofNullable(T value) {
return value == null ? empty() : of(value);
}
从源码可以看出optional为一个单例,两个构造方法都是private型的。
Optional类提供了三个静态方法empty()、of(T value)、ofNullable(T value)来创建Optinal对象,示例如下:
// 1、创建一个包装对象值为空的Optional对象
Optional optStr = Optional.empty();
// 2、创建包装对象值非空的Optional对象
Optional optStr1 = Optional.of("optional");
// 3、创建包装对象值允许为空的Optional对象
Optional optStr2 = Optional.ofNullable(null);
下面以一些典型场景为例,列出Optional API常用接口的用法,并附上相应代码。
get()方法
简单看下get()方法的源码:
public T get() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}
可以看到,get()方法主要用于返回包装对象的实际值,但是如果包装对象值为null,会抛出NoSuchElementException异常。
isPresent()方法
isPresent()方法的源码:
public boolean isPresent() {
return value != null;
}
可以看到,isPresent()方法用于判断包装对象的值是否非空。此方法有点鸡肋,需谨慎使用。
ifPresent()方法
ifPresent()方法的源码:
public void ifPresent(Consumer super T> consumer) {
if (value != null)
consumer.accept(value);
}
ifPresent()方法接受一个Consumer对象(消费函数),如果包装对象的值非空,运行Consumer对象的accept()方法。示例如下:
public static void printName(Student student)
{
Optional.ofNullable(student).ifPresent(u -> System.out.println("The student name is : " + u.getName()));
}
上述示例用于打印学生姓名,由于ifPresent()方法内部做了null值检查,调用前无需担心NPE问题。
filter()方法
filter()方法的源码:
public Optional filter(Predicate super T> predicate) {
Objects.requireNonNull(predicate);
if (!isPresent())
return this;
else
return predicate.test(value) ? this : empty();
}
filter()方法接受参数为Predicate对象,用于对Optional对象进行过滤,如果符合Predicate的条件,返回Optional对象本身,否则返回一个空的Optional对象。举例如下:
public static void filterAge(Student student)
{
Optional.ofNullable(student).filter( u -> u.getAge() > 18).ifPresent(u -> System.out.println("The student age is more than 18."));
}
上述示例中,实现了年龄大于18的学生的筛选。
map()方法
map()方法的源码:
public Optional map(Function super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Optional.ofNullable(mapper.apply(value));
}
}
map()方法的参数为Function(函数式接口)对象,map()方法将Optional中的包装对象用Function函数进行运算,并包装成新的Optional对象(包装对象的类型可能改变)。举例如下:
public static Optional getAge(Student student)
{
return Optional.ofNullable(student).map(u -> u.getAge());
}
上述代码中,先用ofNullable()方法构造一个Optional对象,然后用map()计算学生的年龄,返回Optional对象(如果student为null, 返回map()方法返回一个空的Optinal对象)。
flatMap()方法
flatMap()方法的源码:
public Optional flatMap(Function super T, Optional> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Objects.requireNonNull(mapper.apply(value));
}
}
跟map()方法不同的是,入参Function函数的返回值类型为Optional类型,而不是U类型,这样flatMap()能将一个二维的Optional对象映射成一个一维的对象。以上面map中示例功能为例,进行faltMap()改写如下:
public static Optional getAge(Student student)
{
return Optional.ofNullable(student).flatMap(u -> Optional.ofNullable(u.getAge()));
}
orElse()方法
orElse()方法的源码:
public T orElse(T other) {
return value != null ? value : other;
}
orElse()方法功能比较简单,即如果包装对象值非空,返回包装对象值,否则返回入参other的值(默认值)。如之前提到的代码:
String[] strs = {"1","2"};
System.out.println(Optional.ofNullable(strs).map(str->str.length).orElse(0));
orElseGet()方法
orElseGet()方法的源码:
public T orElseGet(Supplier extends T> other) {
return value != null ? value : other.get();
}
orElseGet()方法与orElse()方法类似,区别在于orElseGet()方法的入参为一个Supplier对象,用Supplier对象的get()方法的返回值作为默认值。如:
String[] strs = {"1","2"};
System.out.println(Optional.ofNullable(strs).map(str->str.length).orElseGet(()->0));
orElseThrow()方法
orElseThrow()方法的源码:
public T orElseThrow(Supplier extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}
orElseThrow()方法其实与orElseGet()方法非常相似了,入参都是Supplier对象,只不过orElseThrow()的Supplier对象必须返回一个Throwable异常,并在orElseThrow()中将异常抛出:
String[] strs = {"1","2"};
System.out.println(Optional.ofNullable(strs).map(str->str.length).orElseThrow(()-> new RuntimeException("0")) );
orElseThrow()方法适用于包装对象值为空时需要抛出特定异常的场景。
参考:
https://www.cnblogs.com/chenglc/p/8087578.html
https://blog.csdn.net/Al_assad/article/details/82356606
https://www.jianshu.com/p/d81a5f7c9c4e#comments