文章目录
- 1. Stream流介绍
- 2. Stream的创建方式
- 3. Stream的中间操作
- 4. Stream的终止操作
- 5. Stream的并行流
- 6. Stream的注意事项
写在最前面,阅读本文需要对
lambda表达式和
方法引用相对熟悉;
1. Stream流介绍
一种支持顺序和并行聚合操作的元素序列,能够对集合、数组进行过滤、排序、去重等操作;
Stream流与日常IO流是不同的技术体系;
官方定义:
A sequence of elements supporting sequential and parallel aggregate operations.
Stream的两种操作:
public class StreamTest {
public static void main(String[] args) {
Stream<String> names = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon");
// 中间操作
Stream<String> namesOfStartsWithJ = names.filter(item -> item.startsWith("j"));
// 终止操作
namesOfStartsWithJ.forEach(System.out::println);
}
}
// 运行结果
jerry
jack
jon
2. Stream的创建方式
- 数组
public class StreamTest {
public static void main(String[] args) {
String[] names = {"tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon"};
// Arrays工具类的静态方法stream()
Stream<String> namesOfStream = Arrays.stream(names);
}
}
- 集合
public class StreamTest {
public static void main(String[] args) {
String[] names = {"tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon"};
List<String> nameList = Arrays.asList(names);
// Collection接口的默认方法stream()和parallelStream()
Stream<String> namesOfStream1 = nameList.stream(); // 顺序流
Stream<String> namesOfStream2 = nameList.parallelStream(); // 并行流
}
}
- Stream接口的静态方法
public class StreamTest {
public static void main(String[] args) {
// Stream接口的静态方法of()
Stream<String> namesOfStream1 = Stream.of("tom");
Stream<String> namesOfStream2 = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon");
// Stream接口的静态方法iterate()和generate()生成的无限流
Stream<Integer> namesOfStream3 = Stream.iterate(1, item -> item + 1); // 迭代方式产生
Stream<Integer> namesOfStream4 = Stream.generate(() -> new Random().nextInt(100)); // 生成方式产生
// Stream接口的静态方法empty()生成的空流
Stream<Integer> namesOfStream5 = Stream.empty();
}
}
3. Stream的中间操作
- 过滤
- filter:对元素进行过滤;
- distinct:对元素进行去重,必须重写元素的hashCode()和equals()方法;
public class StreamTest {
public static void main(String[] args) {
Stream<String> names = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon", "jack");
// 过滤不是'j'开头的名称
names.filter(item -> item.startsWith("j"))
// 去掉重复的名称
.distinct()
.forEach(System.out::println);
}
}
// 运行结果
jerry
jack
jon
- 切片
- limit:截取前面n个元素;
- skip:跳过前面n个元素,当流中元素个数不足n个时,将返回一个空流;
public class StreamTest {
public static void main(String[] args) {
Stream<String> names = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon", "jack");
// 截取前面3个元素
names.limit(3)
// 跳过前面2个元素
.skip(2)
.forEach(System.out::println);
}
}
// 运行结果
jack
- 映射
- map:对元素进行操作,转换为另一种类型;
- flatMap:对元素进行操作,转换为一种Stream流;
public class StreamTest {
public static void main(String[] args) {
Stream<String> names = Stream.of("tom", "jerry", "jack");
// 将string名称转换为User对象
names.map(User::new).forEach(System.out::println);
}
}
class User {
private String name;
public User(String name) {
this.name = name;
}
@Override
public String toString() {
return "User: {name=" + name + "}";
}
}
// 运行结果
User: {name=tom}
User: {name=jerry}
User: {name=jack}
public class StreamTest {
public static void main(String[] args) {
Integer[] arr1 = {1, 2, 3};
Integer[] arr2 = {4, 5, 6};
Integer[] arr3 = {7, 8, 9};
Stream<Integer[]> stream = Stream.of(arr1, arr2, arr3);
// 将二维数组流转换为一维数组流
stream.flatMap(Arrays::stream).forEach(System.out::println);
}
}
// 运行结果
1
2
3
4
5
6
7
8
9
- 排序
- sorted:若元素实现了Comparable接口,重写了compareTo方法,则采用元素内部的方法比较,否则需要传入自定义的比较函数;
public class StreamTest {
public static void main(String[] args) {
Stream<String> names = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon", "jack");
// 将名称进行排序
names.sorted().forEach(System.out::println);
}
}
// 运行结果
bob
jack
jack
jerry
jon
kevin
lisa
mark
tom
4. Stream的终止操作
- 查找
- findFirst:返回第一个元素,注意不是直接返回的元素对象,而是被Optional容器包装的元素对象;
- findAny:返回任意一个元素,注意也是返回的被Optional容器包装的元素对象;
- max:返回元素中的最大值,需要传入比较函数,注意也是返回的被Optional容器包装的元素对象;
- min:返回元素中的最小值,需要传入比较函数,注意也是返回的被Optional容器包装的元素对象;
public class StreamTest {
public static void main(String[] args) {
Stream<Integer> stream1 = Stream.of(1, 2, 3, 4, 5, 6);
Optional<Integer> first = stream1.findFirst();
System.out.println("first: " + first.get());
Stream<Integer> stream2 = Stream.of(1, 2, 3, 4, 5, 6);
Optional<Integer> any = stream2.findAny();
System.out.println("any: " + any.get());
Stream<Integer> stream3 = Stream.of(1, 2, 3, 4, 5, 6);
Optional<Integer> max = stream3.max(Integer::compareTo);
System.out.println("max: " + max.get());
Stream<Integer> stream4 = Stream.of(1, 2, 3, 4, 5, 6);
Optional<Integer> min = stream4.min(Integer::compareTo);
System.out.println("min: " + min.get());
}
}
// 运行结果
first: 1
any: 1
max: 6
min: 1
- 匹配
- allMatch:是否所有元素都匹配;
- anyMatch:是否有任意一个元素匹配;
- noneMatch:是否所有元素都不匹配;
public class StreamTest {
public static void main(String[] args) {
Stream<String> stream1 = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon", "jack");
// 是否所有名称长度都小于6
boolean result1 = stream1.allMatch(item -> item.length() < 6);
System.out.println("result1: " + result1);
Stream<String> stream2 = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon", "jack");
// 是否有名称包含字符'z'
boolean result2 = stream2.anyMatch(item -> item.contains("z"));
System.out.println("result2: " + result2);
Stream<String> stream3 = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon", "jack");
// 是否所有名称都不是'b'开头
boolean result3 = stream3.noneMatch(item -> item.startsWith("b"));
System.out.println("result3: " + result3);
}
}
// 运行结果
result1: true
result2: false
result3: false
- 统计
- count:统计流中元素个数;
public class StreamTest {
public static void main(String[] args) {
Stream<String> names = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon", "jack");
// 统计以'j'开头的名称数量
long size = names.filter(item -> item.startsWith("j")).count();
System.out.println("size: " + size);
}
}
// 运行结果
size: 4
- 遍历
- forEach:遍历元素
public class StreamTest {
public static void main(String[] args) {
Stream<String> names = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon", "jack");
// 遍历以'j'开头的名称
names.filter(item -> item.startsWith("j")).forEach(System.out::println);
}
}
// 运行结果
jerry
jack
jon
jack
- 聚合
- reduce:按照自定义规则对元素进行聚合操作,并返回期待的结果;
public class StreamTest {
public static void main(String[] args) {
Stream<Integer> stream1 = Stream.iterate(1, item -> item + 1);
// 1~100的和
Optional<Integer> sum1 = stream1.limit(100).reduce(Integer::sum);
System.out.println("sum1: " + sum1.get());
Stream<Integer> stream2 = Stream.iterate(1, item -> item + 1);
// 初始值为100, 再加上1~100的和
Integer sum2 = stream2.limit(100).reduce(100, Integer::sum);
System.out.println("sum2: " + sum2);
}
}
// 运行结果
sum1: 5050
sum2: 5150
- 收集
- collect:将已操作过的元素收集起来,可返回List、Set、Map等等;
public class StreamTest {
public static void main(String[] args) {
Stream<Integer> stream1 = Stream.iterate(1, item -> item + 1);
// 将1~10的偶数收集到List中
List<Integer> evenNumbers = stream1.limit(10)
.filter(item -> item % 2 == 0)
.collect(Collectors.toList());
System.out.println("偶数: " + evenNumbers);
Stream<Integer> stream2 = Stream.iterate(1, item -> item + 1);
// 将1~10的奇偶数分类放入map中
Map<String, List<Integer>> numberMap = stream2.limit(10)
.collect(
Collectors.toMap(
item -> item % 2 == 0 ? "偶数" : "奇数",
item -> {
List<Integer> list = new ArrayList<>();
list.add(item);
return list;
},
(oldList, newList) -> {
newList.addAll(oldList);
return newList;
}
)
);
System.out.println("奇偶数分类: " + numberMap);
}
}
// 运行结果
偶数: [2, 4, 6, 8, 10]
奇偶数分类: {偶数=[10, 8, 6, 4, 2], 奇数=[9, 7, 5, 3, 1]}
5. Stream的并行流
- Stream流可以通过实例的parallel和sequential两个方法在并行流和顺序流之间切换;
- Stream并行流是以Java7引入的fork/join框架为基础,以递归方式将可以并行的任务拆分成更小的任务,然后将每个子任务的结果合并起来生成整体结果;
public class StreamTest {
public static void main(String[] args) {
Stream<String> names = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob");
names.parallel().filter(item -> {
System.out.println("filter: " + "thread=" + Thread.currentThread().getName() + ", item=" + item);
return item.startsWith("j");
}).forEach(item -> System.out.println("result: " + item));
}
}
// 运行结果
filter: thread=main, item=lisa
filter: thread=ForkJoinPool.commonPool-worker-2, item=bob
filter: thread=main, item=tom
filter: thread=ForkJoinPool.commonPool-worker-3, item=jack
filter: thread=ForkJoinPool.commonPool-worker-1, item=jerry
filter: thread=ForkJoinPool.commonPool-worker-2, item=mark
result: jerry
result: jack
6. Stream的注意事项
- Stream自己不会存储元素;
- Stream的中间操作不会改变源对象,相反它会返回一个持有结果的新Stream实例;
public class StreamTest {
public static void main(String[] args) {
Stream<String> stream1 = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob");
Stream<String> stream2 = stream1.filter(item -> item.startsWith("j"));
// stream1与stream2是两个不同的对象
System.out.println(stream1);
System.out.println(stream2);
}
}
// 运行结果
java.util.stream.ReferencePipeline$Head@404b9385
java.util.stream.ReferencePipeline$2@6d311334
- Stream的中间操作是延迟加载的,只有当终止操作存在的时候,中间操作才会执行;
public class StreamTest {
public static void main(String[] args) {
Stream<String> stream1 = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob");
Stream<String> stream2 = stream1.filter(item -> {
// 这里的打印不会执行
System.out.println("filter: " + item);
return item.startsWith("j");
});
}
}
- Stream的执行实际上是每一个元素沿着执行链垂直移动的,也就是说当一个元素将执行链执行完成后才会开始第二个元素,但注意排序操作例外;
public class StreamTest {
public static void main(String[] args) {
Stream<Integer> stream1 = Stream.of(3, 1, 2, 5, 6, 4);
stream1.sorted((a, b) -> {
// 全部元素完成排序
System.out.println("sort: " + a + ", " + b);
return Integer.compare(a, b);
}).filter(item -> {
// 1. 对元素过滤
System.out.println("filter: " + item);
return item <= 3;
}).map(item -> {
// 2. 对元素操作
System.out.println("map: " + item * 2);
return item * 2;
}).forEach(item -> {
// 3. 对元素打印
System.out.println("print: " + item);
});
}
}
// 运行结果
sort: 1, 3
sort: 2, 1
sort: 2, 3
sort: 2, 1
sort: 5, 2
sort: 5, 3
sort: 6, 3
sort: 6, 5
sort: 4, 3
sort: 4, 6
sort: 4, 5
filter: 1
map: 2
print: 2
filter: 2
map: 4
print: 4
filter: 3
map: 6
print: 6
filter: 4
filter: 5
filter: 6
- Stream的实例在执行终止操作后不能再执行其他操作,否则会抛出
stream has already been operated upon or closed
异常;