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<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的方法不多,我们一一讲解。我们先总览一下有哪些方法,一看就知道的方法就不说了
点开查看
方法 | 描述 |
---|
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<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()); 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()); 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); } }
|
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); } }
|
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); } }
|
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); } }
|
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); } }
|
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); } }
|
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); } }
|
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); } }
|
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); System.out.println("forEach"); nums.stream().parallel().forEach(System.out::println); System.out.println("forEachOrdered"); nums.stream().parallel().forEachOrdered(System.out::println); } }
|
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(); 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() .collect(Collectors.toList()); 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); System.out.println(result1); boolean result2 = nums.stream().anyMatch(integer -> integer == 5000); 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); System.out.println(result1); boolean result2 = nums.stream().allMatch(integer -> integer > 0); 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); System.out.println(result1); boolean result2 = nums.stream().noneMatch(integer -> integer == 0); 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(); 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()); 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<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<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(); System.out.println("串行流耗时:" + (System.currentTimeMillis() - startTime) + "ms");
startTime = System.currentTimeMillis(); long sum1 = names.parallelStream() .reduce(Integer::sum) .get(); System.out.println("并行流耗时:" + (System.currentTimeMillis() - startTime) + "ms"); } }
|