抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

1. 什么是Stream流?

Stream是一种类似于工厂的流水线的流式思想。在工厂的流水线上,我们可以在每个关口设置不同的条件进行筛选、检查并在尾部输出最终的成品,这里的Stream也可以这样理解。

2. 案例

2.1 找出姓名集合中以张开头的姓名集合

原来我们的解决方案是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Main {
public static void main(String[] args) {
ArrayList<String> names = new ArrayList<>();
names.add("老大");
names.add("小二");
names.add("张三");
names.add("张四");
names.add("王五");
names.add("张玖");
ArrayList<String> list = new ArrayList<>();
names.forEach(s -> {
if (s.startsWith("张")) list.add(s);
});
System.out.println(list);
}
}

而使用Stream后不仅方便,代码量也大大减少,而且代码的可读性也更强

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Main {
public static void main(String[] args) {
ArrayList<String> names = new ArrayList<>();
names.add("老大");
names.add("小二");
names.add("张三");
names.add("张四");
names.add("王五");
names.add("张玖");
// 过滤出以张开头的姓名,并将结果转换成List方便我们处理
List<String> list = names.stream()
.filter(s -> s.startsWith("张"))
.collect(Collectors.toList());
System.out.println(list);
}
}

2.2 复杂的集合操作

假定我们有一组随机生成的整数集合,我们要过滤出50及以内的数,且剔除重复元素后取前10个进行从小到大排序输出。

有点麻烦是吧?但不难,在一般情况下我们是这么操作的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Main {
public static void main(String[] args) {
ArrayList<Integer> numList = new ArrayList<>(200);
Random random = new Random();
for (int i = 0; i < 200; i++) {
numList.add(random.nextInt(101));
}
ArrayList<Integer> list = new ArrayList<>();
numList.forEach(num -> {
if (num <= 50 && !list.contains(num)) list.add(num);
});
List<Integer> tenNumbers = list.subList(0, 10);
tenNumbers.sort(Comparator.comparingInt(o -> o));
System.out.println(tenNumbers);
}
}

在使用Stream进行处理后,代码就变得十分简洁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Main {
public static void main(String[] args) {
ArrayList<Integer> numList = new ArrayList<>(200);
Random random = new Random();
for (int i = 0; i < 200; i++) {
numList.add(random.nextInt(101));
}
List<Integer> list = numList.stream()
.filter(num -> num <= 50)
.distinct()
.limit(10)
.sorted()
.collect(Collectors.toList());
System.out.println(list);
}
}

是不是很Easy?

3. 方法使用

那么我们怎么使用Stream方便我们的开发呢?Stream的方法不多,我们一一讲解。我们先总览一下有哪些方法,一看就知道的方法就不说了

测试环境:JDK11

点开查看
方法描述
builder返回一个构造器
of构造Stream流
filter过滤出符合要求的元素
map将流中的元素映射到另一个流中
mapToInt将流中元素转成int类型,其他相似方法同理
flatMap合并数据
flatMapToInt合并数据并转换成int类型,其他相似方法同理
distinct剔除重复元素
sorted从小到大排序,sorted(Comparator<? super T> comparator)为按一定规则进行排序
peek在流(一个步骤)工作之前插入一个操作,但在这里改变元素并不会生效
limit获取前n个元素,如果总元素小于n,则不进行操作
skip跳过前n个元素
takeWhile逐个获取符合规则的元素,遇到不符合的立马结束操作,丢弃后面的所有元素
dropWhile逐个删除符合规则的元素,遇到不符合的立马结束操作,返回剩下的所有元素
forEach遍历元素,在并行流中输出元素不保证与原来的一致
forEachOrdered在并行流中保证输出顺序一致
reduce对Stream元素进行聚合求值
collect将流转成你想要的类型
anyMatch只要有一个元素符合规则就返回True
allMatch只有每个元素均符合规则时才会返回True
noneMatch只有每个元素都不符合规则时返回True
findAny随便返回一个元素,没错,你没看错

3.1 获取Stream流的三种方法

3.1.1 builder

1
2
3
4
5
6
7
8
public class Main {
public static void main(String[] args) {
Stream.Builder<Integer> builder = Stream.builder();
builder.add(1);
builder.add(2);
Stream<Integer> stream = builder.build();
}
}

3.1.2 of

1
2
3
4
5
6
7
8
9
10
public class Main {
public static void main(String[] args) {
Stream<String> stream1 = Stream.of("张三", "李四", "王五");

Integer[] numbers = new Integer[]{1, 2, 3, 4, 5, 6};
Stream<Integer> stream2 = Stream.of(numbers);

Stream<String> stream3 = Stream.ofNullable(null);
}
}

3.1.3 Collection.stream()

任何实现Collection接口的类均可调用stream()获取到Stream流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Main {
public static void main(String[] args) {
List<Integer> nums = new ArrayList<>();
Stream<Integer> stream1 = nums.stream();

Set<String> set = new HashSet<>();
Stream<String> stream2 = set.stream();

Vector<String> vector = new Vector<>();
Stream<String> stream3 = vector.stream();

// Map不是Collection的子接口,所以需要分别处理
Map<String, String> map = new HashMap<>();
Stream<String> keyStream = map.keySet().stream();
Stream<String> valueStream = map.values().stream();
Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream();
}
}

3.2 map

将流中的元素映射到另一个流中,在这中间我们可以对元素进行处理,如以下代码是将元素转成int类型,且将456改成999

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Main {
public static void main(String[] args) {
ArrayList<String> strList = new ArrayList<>();
strList.add("123");
strList.add("456");
strList.add("789");
List<Integer> nums = strList.stream().map(
s -> {
if (s.equals("456")) return 999;
else return Integer.parseInt(s);
}
).collect(Collectors.toList());
// 输出[123, 999, 789]
System.out.println(nums);
}
}

3.3 mapToInt

将流中元素转成int类型,其他相似方法同理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Main {
public static void main(String[] args) {
ArrayList<String> strList = new ArrayList<>();
strList.add("123");
strList.add("456");
strList.add("789");
List<Integer> nums = strList.stream()
.mapToInt(Integer::parseInt)
// 注意要写上这句
.boxed()
.collect(Collectors.toList());
// 输出[123, 456, 789]
System.out.println(nums);
}
}

3.4 flatMap

把两个列表的数据合并成一个列表数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Main {
public static void main(String[] args) {
List<List<Integer>> lists = new ArrayList<>();
List<Integer> list = new ArrayList<>();
list.add(4444);
list.add(33333);
list.add(444444);
// 添加两次
lists.add(list);
lists.add(list);
System.out.println(lists);
List<Integer> result = lists.stream().flatMap(Collection::stream).collect(Collectors.toList());
System.out.println(result);
// 输出
// [[4444, 33333, 444444], [4444, 33333, 444444]]
// [4444, 33333, 444444, 4444, 33333, 444444]
}
}

3.5 flatMapToInt

把两个列表的数据合并成一个列表数据并将数据转成int类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Main {
public static void main(String[] args) {
List<List<Integer>> lists = new ArrayList<>();
List<Integer> list = new ArrayList<>();
list.add(4444);
list.add(33333);
list.add(444444);
// 添加两次
lists.add(list);
lists.add(list);
System.out.println(lists);
List<Integer> result = lists.stream()
.flatMapToInt(integers -> integers.stream().mapToInt(value -> value))
.boxed()
.collect(Collectors.toList());
System.out.println(result);
// 输出
// [[4444, 33333, 444444], [4444, 33333, 444444]]
// [4444, 33333, 444444, 4444, 33333, 444444]
}
}

3.6 filter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Main {
public static void main(String[] args) {
ArrayList<String> names = new ArrayList<>();
names.add("老大");
names.add("小二");
names.add("张三");
names.add("张四");
names.add("王五");
names.add("张玖");
ArrayList<String> list = new ArrayList<>();
names.forEach(s -> {
if (s.startsWith("张")) list.add(s);
});
// 输出[张三, 张四, 张玖]
System.out.println(list);
}
}

3.7 sorted

对元素进行排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Main {
public static void main(String[] args) {
List<Integer> nums = new ArrayList<>();
nums.add(5);
nums.add(8);
nums.add(18);
nums.add(1);
nums.add(15);
nums.add(20);
nums.add(3);
System.out.println(nums);
List<Integer> list1 = nums.stream()
.sorted()
.collect(Collectors.toList());
List<Integer> list2 = nums.stream()
.sorted((o1, o2) -> o2 - o1)
.collect(Collectors.toList());
System.out.println(list1);
System.out.println(list2);
// 输出
// [5, 8, 18, 1, 15, 20, 3]
// [1, 3, 5, 8, 15, 18, 20]
// [20, 18, 15, 8, 5, 3, 1]
}
}

3.8 peek

在流(一个步骤)工作之前插入一个操作,但在这里改变元素并不会生效

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Main {
public static void main(String[] args) {
List<Integer> nums = new ArrayList<>();
nums.add(5);
nums.add(8);
nums.add(18);
nums.add(1);
nums.add(15);
nums.add(20);
nums.add(3);
nums.stream()
.peek(integer -> System.out.println("当前在处理:" + integer))
.sorted()
.peek(integer -> System.out.println("当前在准备输出:" + integer))
.forEach(System.out::println);
}
}

控制台

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
当前在处理:5
当前在处理:8
当前在处理:18
当前在处理:1
当前在处理:15
当前在处理:20
当前在处理:3
当前在准备输出:1
1
当前在准备输出:3
3
当前在准备输出:5
5
当前在准备输出:8
8
当前在准备输出:15
15
当前在准备输出:18
18
当前在准备输出:20
20

3.9 limit

获取前n个元素,如果总元素小于n,则不进行操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Main {
public static void main(String[] args) {
List<Integer> nums = new ArrayList<>();
nums.add(5);
nums.add(8);
nums.add(18);
nums.add(1);
nums.add(15);
nums.add(20);
nums.add(3);
System.out.println(nums);
List<Integer> list1 = nums.stream()
.limit(3)
.collect(Collectors.toList());
List<Integer> list2 = nums.stream()
.limit(100)
.collect(Collectors.toList());
System.out.println(list1);
System.out.println(list2);
// 输出
// [5, 8, 18, 1, 15, 20, 3]
// [5, 8, 18]
// [5, 8, 18, 1, 15, 20, 3]
}
}

3.10 skip

跳过前n个元素,元素小于等于n个则返回一个空流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Main {
public static void main(String[] args) {
List<Integer> nums = new ArrayList<>();
nums.add(5);
nums.add(8);
nums.add(18);
nums.add(1);
nums.add(15);
nums.add(20);
nums.add(3);
System.out.println(nums);
List<Integer> list = nums.stream()
.skip(4)
.collect(Collectors.toList());
System.out.println(list);
// 输出
// [5, 8, 18, 1, 15, 20, 3]
// [15, 20, 3]
}
}

3.11 takeWhile

逐个获取符合规则的元素,遇到不符合的立马结束操作,丢弃后面的所有元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Main {
public static void main(String[] args) {
List<Integer> nums = new ArrayList<>();
nums.add(5);
nums.add(8);
nums.add(18);
nums.add(1);
nums.add(15);
nums.add(20);
nums.add(3);
System.out.println(nums);
List<Integer> list = nums.stream()
.takeWhile(integer -> integer < 10)
.collect(Collectors.toList());
System.out.println(list);
// 输出
// [5, 8, 18, 1, 15, 20, 3]
// [5, 8]
}
}

3.12 dropWhile

逐个删除符合规则的元素,遇到不符合的立马结束操作,返回剩下的所有元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Main {
public static void main(String[] args) {
List<Integer> nums = new ArrayList<>();
nums.add(5);
nums.add(8);
nums.add(18);
nums.add(1);
nums.add(15);
nums.add(20);
nums.add(3);
System.out.println(nums);
List<Integer> list = nums.stream()
.dropWhile(integer -> integer != 1)
.collect(Collectors.toList());
System.out.println(list);
// 输出
// [5, 8, 18, 1, 15, 20, 3]
// [1, 15, 20, 3]
}
}

3.13 forEach

遍历元素,在并行流中输出元素不保证与原来的一致

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Main {
public static void main(String[] args) {
List<Integer> nums = new ArrayList<>();
nums.add(5);
nums.add(8);
nums.add(18);
nums.add(1);
nums.stream().forEach(System.out::println);
// 输出
// 5
// 8
// 18
// 1
}
}

3.14 forEachOrder

在并行流中保证输出顺序一致

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class Main {
public static void main(String[] args) {
List<Integer> nums = new ArrayList<>();
nums.add(5);
nums.add(8);
nums.add(18);
nums.add(1);
System.out.println(nums);
// forEach 发现顺序和原来的不一样
System.out.println("forEach");
nums.stream().parallel().forEach(System.out::println);
// forEachOrdered 顺序和原来的一样
System.out.println("forEachOrdered");
nums.stream().parallel().forEachOrdered(System.out::println);
// 输出
// [5, 8, 18, 1]
// forEach
// 18
// 1
// 5
// 8
// forEachOrdered
// 5
// 8
// 18
// 1
}
}

3.15 reduce

对Stream元素进行归约

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Main {
public static void main(String[] args) {
List<Integer> nums = new ArrayList<>();
nums.add(5);
nums.add(8);
nums.add(18);
nums.add(1);
long sum = nums.stream().reduce(Integer::sum).get();
// 5+8+18+1=32
// 输出 32
System.out.println(sum);
}
}

3.16 collect

将流转成你想要的类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Main {
public static void main(String[] args) {
ArrayList<String> strList = new ArrayList<>();
strList.add("123");
strList.add("456");
strList.add("789");
List<Integer> nums = strList.stream()
.mapToInt(Integer::parseInt)
.boxed()
// 转换成List<Integer>集合
.collect(Collectors.toList());
// 输出[123, 456, 789]
System.out.println(nums);
}
}

3.17 anyMatch

只要有一个元素符合规则就返回True

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Main {
public static void main(String[] args) {
List<Integer> nums = new ArrayList<>();
nums.add(5);
nums.add(8);
nums.add(18);
nums.add(1);
boolean result1 = nums.stream().anyMatch(integer -> integer == 8);
// True
System.out.println(result1);
boolean result2 = nums.stream().anyMatch(integer -> integer == 5000);
// False
System.out.println(result2);
}
}

3.18 allMatch

只有每个元素均符合规则时才会返回True

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Main {
public static void main(String[] args) {
List<Integer> nums = new ArrayList<>();
nums.add(5);
nums.add(8);
nums.add(18);
nums.add(1);
boolean result1 = nums.stream().allMatch(integer -> integer < 8);
// False
System.out.println(result1);
boolean result2 = nums.stream().allMatch(integer -> integer > 0);
// True
System.out.println(result2);
}
}

3.19 noneMatch

只有每个元素都不符合规则时返回True

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Main {
public static void main(String[] args) {
List<Integer> nums = new ArrayList<>();
nums.add(5);
nums.add(8);
nums.add(18);
nums.add(1);
boolean result1 = nums.stream().noneMatch(integer -> integer < 8);
// False
System.out.println(result1);
boolean result2 = nums.stream().noneMatch(integer -> integer == 0);
// True
System.out.println(result2);
}
}

3.20 findAny

随便返回一个元素,没错,你没看错

1
2
3
4
5
6
7
8
9
10
11
12
public class Main {
public static void main(String[] args) {
List<Integer> nums = new ArrayList<>();
nums.add(5);
nums.add(8);
nums.add(18);
nums.add(1);
int ele = nums.stream().findAny().get();
// 输出8
System.out.println(ele);
}
}

3.21 distinct

对元素进行去重操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Main {
public static void main(String[] args) {
List<Integer> nums = new ArrayList<>();
nums.add(5);
nums.add(5);
nums.add(8);
nums.add(8);
nums.add(18);
nums.add(18);
nums.add(1);
nums.add(1);
System.out.println(nums);
List<Integer> list = nums.stream()
.distinct()
.collect(Collectors.toList());
// 输出
// [5, 5, 8, 8, 18, 18, 1, 1]
// [5, 8, 18, 1]
System.out.println(list);
}
}

4. 并行流、串行流

4.1 串行流

这是案例里的代码,此时我们是串行流,即在一条线程中处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Main {
public static void main(String[] args) {
ArrayList<String> names = new ArrayList<>();
names.add("老大");
names.add("小二");
names.add("张三");
names.add("张四");
names.add("王五");
names.add("张玖");
// 过滤出以张开头的姓名,并将结果转换成List方便我们处理
List<String> list = names.stream()
.filter(s -> s.startsWith("张"))
.collect(Collectors.toList());
System.out.println(list);
}
}
1
2
3
4
5
6
7
Thread[main,5,main]
Thread[main,5,main]
Thread[main,5,main]
Thread[main,5,main]
Thread[main,5,main]
Thread[main,5,main]
[张三, 张四, 张玖]

4.2 并行流

parallelStream其实就是一个并行执行的流。它通过默认的ForkJoinPool,可能提高多线程任务的速度。可以通过以下方式获取

1
2
3
4
5
ArrayList<Integer> list = new ArrayList<>();
// 直接获取并行的流
Stream<Integer> stream = list.parallelStream();
// 将串行流转成并行流
Stream<Integer> stream = list.stream().parallel();

并行执行效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Main {
public static void main(String[] args) {
ArrayList<String> names = new ArrayList<>();
names.add("老大");
names.add("小二");
names.add("张三");
names.add("张四");
names.add("王五");
names.add("张玖");
// 过滤出以张开头的姓名,并将结果转换成List方便我们处理
List<String> list = names.stream()
.parallel()
.filter(s -> {
System.out.println(Thread.currentThread());
return s.startsWith("张");
})
.collect(Collectors.toList());
System.out.println(list);
}
}
1
2
3
4
5
6
7
Thread[main,5,main]
Thread[ForkJoinPool.commonPool-worker-19,5,main]
Thread[ForkJoinPool.commonPool-worker-23,5,main]
Thread[ForkJoinPool.commonPool-worker-5,5,main]
Thread[ForkJoinPool.commonPool-worker-9,5,main]
Thread[ForkJoinPool.commonPool-worker-19,5,main]
[张三, 张四, 张玖]

4.3 执行效率对比

在百万级数据处理时,串行流和并行流的耗时分别为131ms和55ms

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Main {
public static void main(String[] args) {
ArrayList<Integer> names = new ArrayList<>(10000000);
Random random = new Random();
for (int i = 0; i < 10000000; i++) {
names.add(random.nextInt(1000));
}
long startTime = System.currentTimeMillis();
long sum = names.stream()
.reduce(Integer::sum)
.get();
// 131ms
System.out.println("串行流耗时:" + (System.currentTimeMillis() - startTime) + "ms");

startTime = System.currentTimeMillis();
long sum1 = names.parallelStream()
.reduce(Integer::sum)
.get();
// 55ms
System.out.println("并行流耗时:" + (System.currentTimeMillis() - startTime) + "ms");
}
}

评论