流(Stream)
1. 流的概念
流是
数据渠道
,用于操作数据源
,所生成一种新的元素序列
。集合讲的是数据,流讲的是计算
,是操作
。
Stream是Java8中处理集合
的关键抽象概念,它可以指定希望对集合的操作
,可以执行复杂的查找
、过滤和映射数据
等操作。
使用Stream API 对集合的数据进行操作
,类似于SQL执行的数据库查询
,也可以用来并行执行
操作,其提供了一种高效且易于使用的处理数据方式
。
注意点:
- Stream自身
不会存储元素
- Stream
不会改变数据源对象
,相反会返回产生一个持有结果的新Stream
- Steam操作是
延迟执行
的,这意味着他们会等到需要结果的时候才执行
。
2. 流的操作步骤
三步走
2.1. 创建Stream
获取一个数据源(集合,数组),从而获取一个流
产生方式:
2.1.1 通过Collection 系列集合
提供的串行流:stream()
、并行流: paralleStream()
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
2.1.2 通过Arrays
中的静态方法stream(T[] array)
获取数组流
Arrays.stream(T[] array)的源码:
public static <T> Stream<T> stream(T[] array) {
return stream(array, 0, array.length);
}
用例:
Stu[] stus = new Stu[10];
Stream<Stu> stream2 = Arrays.stream(stus);
/*
public static <T> Stream<T> stream(T[] array) {
return stream(array, 0, array.length);
}
*/
2.1.3 通过Stream类
中的静态方法 of()
Stream.of() 源码:
//1.单参泛型of
public static<T> Stream<T> of(T t) {
return StreamSupport.stream(new Streams.StreamBuilderImpl<>(t), false);
}
//2.可变参数
@SafeVarargs
@SuppressWarnings("varargs") // Creating a stream from an array is safe
public static<T> Stream<T> of(T... values) {
return Arrays.stream(values);
}
用例:
Stream<String> stream3 = Stream.of("hxh", "aj", "hhh");
2.1.4 使用Stream
类的静态方法 iterate
创建无限流
iterate方法:
Stream<T> iterate(final T seed, final UnaryOperator<T> f)
参数 seed 种子起始值
,UnaryOperator
函数式接口 继承Function<T,T>
此时参数类型符合返回值类型一致
用例:
//4.使用Stream类的静态方法 iterate 创建无限流
//Stream<T> iterate(final T seed, final UnaryOperator<T> f)
//参数 seed 种子起始值,
// UnaryOperator 函数式接口 继承Function<T,T> 此时参数类型符合返回值类型一致
Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 2);
//中间操作和终止操作
stream4.limit(5).forEach(System.out::println);
//0
//2
//4
//6
//8
2.1.5 使用Stream
类的静态方法 generate
创建无限流
generate方法参数为Supplier<T>
供给型接口
//5.使用Stream类的静态方法 generate 创建无限流
//参数为Supplier<T> 供给型接口
Stream<Double> generateStream = Stream.generate(() -> Math.random());
generateStream.limit(5).forEach(System.out::println);
//0.4762976596937549
//0.08577913333772513
//0.32149010682857515
//0.31059489250233197
//0.45181354173159927
2.2. 用Stream中间操作
一个中间操作链,用Stream API 对数据源数据进行操作处理
注意点:
- 若只有中间操作,则
不会执行
- 只有终止操作执行后,所有的中间操作
一次执行
,此时就称为延迟加载
或者惰性求值
验证是否是延迟加载:
@Test
public void test2(){
//取age>30的Stu元素
//若只有中间操作,则不会执行
Stream<Stu> stuStream = stuList.stream().filter((i) -> {
System.out.println("验证是否是延迟加载");
return i.getAge() > 40;
});
//此时只有中间操作,无终止操作,无结果,控制台无输出
}
此时只有中间操作,无终止操作
,无结果,控制台无输出
。
此时加上终止操作后:
@Test
public void test2(){
//取age>30的Stu元素
//若只有中间操作,则不会执行
Stream<Stu> stuStream = stuList.stream().filter((i) -> {
System.out.println("验证是否是延迟加载");
return i.getAge() > 40;
});
//终止操作 执行后,所有的中间操作一次执行,此时就称为延迟加载或者惰性求值
stuStream.forEach(System.out::println);
}
此时结果为:
验证是否是延迟加载
验证是否是延迟加载
验证是否是延迟加载
验证是否是延迟加载
Stu{
id=4, name='cc', age=42}
验证是否是延迟加载
Stu{
id=5, name='dd', age=52}
结论:若只有中间操作,则不会执行中间操作。终止操作 执行后,所有的中间操作一次执行。最后流中只有经过操作过滤后的元素。
2.2.1 筛选与切片
迭代:
- 内部迭代:迭代过程操作由Stream API 内部
自主完成,无需自行再次编写。
- 外部迭代:由程序编写人员
自己通过一些迭代方法进行的迭代操作。
2.2.1.1 filter
-过滤
Stream<T> filter(Predicate<? super T> predicate)
断言型
接口参数 即条件判断过滤
用例:
先创建一个Stu类List集合
List<Stu> stuList = Arrays.asList(
new Stu(1,"hh",22),
new Stu(2,"aa",22),
new Stu(3,"bb",32),
new Stu(4,"cc",42),
new Stu(5,"dd",52)
);
filter过滤实现: