当前位置 博文首页 > 龙台的技术笔记:Java8 Stream写给自己的小白流式操作
Java8
的出现可以说是 Java
面向函数式编程前进的一大步。刨除了很多冗余操作,间接的解放了双手👋
在看本篇文章时如果对 Java8的函数式接口不了解可以移步 Java8 Functional Interface写给自己的小白函数式接口说明
Java8 Stream
采用的是函数式编程方式,使用函数式编程方式在集合类上进行复杂操作的工具,可以更容易的使用 Lambda
表达式的形式书写,就看下流式操作正确的打开方式
对于不太清楚Stream流式操作的朋友而言,可以尽情 XX 了 😁
另附一张归结的思维导图,可以查看是否有自己需要的 API
创建流的方式有很多种,比如:Arrays.stream
、Stream.of
、Collection.stream
、Stream.iterate
、Stream.generate
,流的创建方式有很多,比较常用的则是根据 Collection.stream()
,从集合中创建流
static <T> Stream<T> stream(T[] array)
static<T> Stream<T> of(T... values)
default Stream<E> stream()
static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
static<T> Stream<T> generate(Supplier<T> s)
第一种stream方法是
Arrays.stream
| 第三种是Collection.stream
Arrays.stream
通过 Arrays
的静态方法来进行流的创建,参数为一个范型数组
String[] strArray = { "龙台", "百凯", "英雄" };
Arrays.stream(strArray).forEach(System.out::println);
// 顺序输出 龙台 百凯 英雄
Stream.of
传入一个泛型数组,或者多个参数,创建一个流,底层也是调用了 Arrays
的 stream
静态方法
String[] strArray = { "龙台", "百凯", "英雄" };
Stream.of(strArray).forEach(System.out::println);
// 顺序输出 龙台 百凯 英雄
Collection.stream
可以使用集合的接口的默认方法。使用这个方法,包括继承 Collection
的接口,如:Set,List,Map,SortedSet
等等都可以创建流
List<String> strList = Arrays.asList("龙台", "百凯","英雄");
strList.stream().forEach(System.out::println);
// 顺序输出 龙台 百凯 英雄
Stream.iterate
该方式是在 Stream
接口下的静态方法,见名识义可以看出是以迭代器的方式创建一个流
Stream.iterate(1, each -> each + 1)
.limit(3)
.collect(Collectors.toList())
.forEach(System.out::println);
// 顺序输出 1 2 3
Stream.generate
同样是 Stream
接口下的静态方法,参数就是一个 Supplier
的供给型的参数
Stream.generate(Math::random)
.limit(5)
.forEach(System.out::println);
// 依次打印5个随机数
Stream
流的系列流程可以总结分为以下操作
数据源(source) -> 数据转换/处理(中间操作) -> 结果处理(终端操作)
数据源指的就是通过以上几种方式生成的Stream
流。需要注意的是:终端操作一旦执行,那么整个流的生命周期就结束了
,无法再进行计算或再次汇总
??????之前我也认为,会顺序输出 龙台 百凯
两个字符串,但是实际上是一个也不会输出,因为当前代码中 没有包含终端操作
Stream.of("龙台", "百凯")
.filter(each -> {
System.out.println(each);
return true;
});
如果说在👆代码加以点缀,那么就变得不一样了
Stream.of("龙台", "百凯")
.filter(each -> {
System.out.println("filter:" + each);
return true;
})
.forEach(each -> System.out.println("forEach:" + each));
}
filter:龙台
forEach:龙台
filter:百凯
forEach:百凯
之前一直认为是执行完
filter
操作,然后才会执行接下来的forEach
👋👋👋的打脸
Stream
包含中间操作,这个在使用上也是一个坑,那就是流的 延迟性
,也叫做 惰性操作
实际上在处理 龙台
元素的时候,执行完 filter
就会执行 forEach
,然后继续执行 百凯
元素
查阅资料说明 这种设计的方式是出于性能考虑
Stream.of("龙台", "百凯", "英雄")
.map(each -> {
System.out.println("map: " + each);
return each;
})
.anyMatch(each -> {
System.out.println("anyMatch: " + each);
return each.startsWith("百"); // 过滤以 '百' 开头的元素
});
}
map: 龙台
anyMatch: 龙台
map: 百凯
anyMatch: 百凯
终端操作 anyMatch()
表示如果发现以 '百'
开头的元素,停止执行直接返回,所以 '英雄'
元素就没有被循环 ?? 执行
如果仅仅是上面的方式可能无法直观的看出性能差距在哪,接下来以 map
、filter
的方式来进行操作
Stream.of("龙台", "百凯", "驷马", "狂妄", "偏执", "英雄")
.map(each -> {
System.out.println("map: " + each);
return each;
})
.filter(each -> {
System.out.println("filter: " + each);
return each.startsWith("英");
})
.forEach(each -> System.out.println("forEach: " + each));
map: 龙台
filter: 龙台
map: 百凯
filter: 百凯
map: 驷马
filter: 驷马
map: 狂妄
filter: 狂妄
map: 偏执
filter: 偏执
map: 英雄
filter: 英雄
forEach: 英雄
通过打印得知,执行顺序分别是一次 map
、filter
,如果将 filter
和 map
的执行顺序转换一下的话
Stream.of("龙台", "百凯", "驷马", "狂妄", "偏执", "英雄")
.filter(each -> {
System.out.println("filter: " + each);
return each.startsWith("英");
})
.map(each -> {
System.out.println("map: " + each);
return each;
})
.forEach(each -> System.out.println("forEach: " + each));
filter: 龙台
filter: 百凯
filter: 驷马
filter: 狂妄
filter: 偏执
filter: 英雄
map: 英雄
forEach: 英雄
根据
Stream
的执行方式来正确的定义函数API的顺序,如果集合包含大量元素的情况下,这种性能提升还是很可观的
值得一提的是Stream
的sorted
函数是水平执行的,会首先将集合中的内容全部在sorted
方法中执行后才会继续向下执行
一个流在被创建后,可以使用多个数据处理、转换操作,全部操作之后返回给终端操作的是一个新的流
在日常工作中通常会遇到这个场景,需要将一个集合对象中的某个属性提取出来重新组合成为一个新的集合,
Stream
中的map
和flatMap
可以很好的完成这个需求,关键是:代码还很简洁 ??
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
map()
将学生集合中的学生名称抽取映射成为一个新的只包含名称的集合
@Data
public class Student {
private String num, name, area;
public Student(String num, String name, String area) {
this.area = area; this.name = name; this.num = num;
}
public static void main