注:以下内容基于Java 8,所有代码都已在Java 8环境下测试通过
目录:
Stream(流)
是一个来自数据源的元素队列并支持聚合操作
Stream
是 Java8 中处理集合的关键抽象概念,它可以指定对集合进行的操作,可以执行非常复杂的查找、筛选、过滤、排序、聚合和映射数据等操作。通过配合 Lambda
,Stream
方便的操作集合(Collection)。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
特点:
不仅支持串行操作,还支持并行操作,简化了编写并行程序的过程
不是数据结构,不会保存数据(类似工厂的流水线,只对产品进行加工而不会储存产品)
惰性求值,流在中间操作过程中,只是对操作进行了记录,并不会立即执行,等到执行终端操作的时候才会进行实际的计算
使用流程:
从支持数据处理操作的源生成元素序列.数据源可以是集合,数组或IO资源。
从操作角度来看,流与集合是不同的. 流不存储数据值; 流的目的是处理数据,它是关于算法与计算的。
如果把集合作为流的数据源,创建流时不会导致数据流动; 如果流的终止操作需要值时,流会从集合中获取值; 流只使用一次。
流中心思想是延迟计算,流直到需要时才计算值。
主要有以下几种方式创建Stream。
import java.util.ArrayList;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(-1);
list.add(1);
Stream<Integer> listStream = list.stream();//通过集合创建串行流
Stream<Integer> parallelListStream = list.parallelStream();//通过集合创建并行流
}
}
串行流在处理时,由一个线程按照顺序对其执行操作;而并行流在处理时,可以由多个线程对流并行操作,可以加快处理的速度。
import java.util.Arrays;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
int[] intArr = {0, 1, 2};
double[] doubleArr = {1.1, 2.2, 3.3};
IntStream intStream = Arrays.stream(intArr);//从 int 数组创建数值流
DoubleStream doubleStream = Arrays.stream(doubleArr);//从 double 数组创建数值流
}
}
该方法生成的是数值流( IntStream
、DoubleStream
),使用数值流可以避免计算过程中的拆箱装箱从而提高性能。
Stream API提供了 mapToInt
、 mapToDouble
、 mapToLong
三种方式将对象流(Stream)转换成对应的数值流,同时提供了 boxed
方法将数值流转换为对象流:
import java.util.ArrayList;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(-1);
list.add(1);
Stream<Integer> listStream = list.stream();//通过集合创建流
IntStream intStream = listStream.mapToInt(Math::abs);//将流转换为数值流
Stream<Integer> stream = intStream.boxed();//将数值流转换为流
}
}
主要有以下三种:
of()
:将任意对象转换成 Stream
T... values
:用于创建流的数据iterator()
:流迭代器,会一直迭代下去生成无限流,因此一般要结合 limit
方法指定流中元素的数量防止一直迭代
seed
:初始元素f
:函数,前一个元素使用该函数生成新的元素generator()
:流生成器,根据参数生成无限流,一般要结合 limit
方法指定流中元素的数量防止一直迭代
s
:实现 Supplier
函数式接口 ,为流提供值import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
Stream<Integer> stream1 = Stream.of(0, 1, 2);//将 int 转换成 Stream
Stream<int[]> stream2 = Stream.of(new int[]{0, 1, 2});//将 int 数组转换成 Stream
/**
* 生成偶数
* limit(8) 表示只生成 8 个
*/
Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2).limit(8);
/**
* 生成字符串
* limit(3) 表示只生成 3 个
*/
Stream<String> stream4 = Stream.generate(() -> "Hello World").limit(3);
}
}
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
try {
Stream<String> fileStream = Files.lines(Paths.get("data.txt"), Charset.defaultCharset());
} catch (Exception e) {
e.printStackTrace();
}
}
}
得到的 Stream
是文件中的每一行。
除了可以直接通过集合创建并行流,还可以通过 parallel()
把顺序流转换成并行流。
import java.util.ArrayList;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(-1);
list.add(1);
Stream<Integer> listStream = list.stream();//通过集合创建串行流
Stream<Integer> parallelListStream = list.parallelStream();//通过集合直接创建并行流
Stream<Integer> parallelListStream1 = listStream.parallel();//将串行流转换成并行流
Stream<Integer> listStream1 = parallelListStream1.sequential();//将并行流转换成串行流
}
}
Stream的操作大致可以分为中间操作和终端操作两类。
操作类型 | 常用API |
---|---|
无状态操作 | unordered()、peek()、map()、mapToInt()、mapToLong()、mapToDouble()、filter()、filterMap()、filterMapToInt()、filterMapToLong()、filterMapToDouble() |
有状态操作 | limit()、distinct()、sorted()、skip() |
短路操作 | anyMatch()、allMatch()、findFirst()、findAny()、、 |
非短路操作 | forEach()、forEachOrdered()、toArray()、reduce()、collect()、max()、min()、count()、forEachOrdered() |
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
Stream<Integer> stream = Stream.iterate(0, (x) -> x + 1).limit(20);//从 0 开始生成 20 个整数
stream = stream.filter(x -> x > 10)//过滤出大于10的数
.map(x -> x - 20);//将过滤出的数减 20
stream.forEachOrdered(System.out::println);//遍历输出
}
}