如何使用Stream?
聚合操做是Java 8针对集合类,使编程更为便利的方式,能够与Lambda表达式一块儿使用,达到更加简洁的目的。java
前面例子中,对聚合操做的使用能够归结为3个部分:编程
1) 建立Stream:经过stream()方法,取得集合对象的数据集。
2) Intermediate:经过一系列中间(Intermediate)方法,对数据集进行过滤、检索等数据集的再次处理。如上例中,使用filter()方法来 对数据集进行过滤。
3) Terminal经过最终(terminal)方法完成对数据集中元素的处理。如上例中,使用forEach()完成对过滤后元素的打印。
在一次聚合操做中,能够有多个Intermediate,可是有且只有一个Terminal。也就是说,在对一个Stream能够进行屡次转换操做,并非每次都对Stream的每一个元素执行转换。并不像for循环中,循环N次,其时间复杂度就是N。转换操做是lazy(惰性求值)的,只有在Terminal操做执行时,才会一次性执行。能够这么认为,Stream 里有个操做函数的集合,每次转换操做就是把转换函数放入这个集合中,在 Terminal 操做的时候循环 Stream 对应的集合,而后对每一个元素执行全部的函数。数组
Stream的操做分类
刚才提到的Stream的操做有Intermediate、Terminal和Short-circuiting:并发
- Intermediate:map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 skip、 parallel、 sequential、 unordered
- Terminal:forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、iterator
- Short-circuiting: anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit
一、建立流
若是是数组的话,可使用 Arrays.stream()
或者 Stream.of()
建立流;若是是集合的话,能够直接使用 stream()
方法建立流,由于该方法已经添加到 Collection 接口中。ide
public class CreateStreamDemo { public static void main(String[] args) { String[] arr = new String[]{"武汉加油", "中国加油", "世界加油"}; Stream<String> stream = Arrays.stream(arr); stream = Stream.of("武汉加油", "中国加油", "世界加油"); List<String> list = new ArrayList<>(); list.add("武汉加油"); list.add("中国加油"); list.add("世界加油"); stream = list.stream(); } }
查看 Stream 源码的话,你会发现 of() 方法内部其实调用了 Arrays.stream() 方法。函数
public static<T> Stream<T> of(T... values) { return Arrays.stream(values); }
另外,集合还能够调用 parallelStream() 方法建立并发流,默认使用的是 ForkJoinPool.commonPool()线程池。工具
List<Long> aList = new ArrayList<>(); Stream<Long> parallelStream = aList.parallelStream();
二、操做流
Stream 类提供了不少有用的操做流的方法,我来挑一些经常使用的给你介绍一下。ui
1)过滤线程
经过 filter() 方法能够从流中筛选出咱们想要的元素。code
public class FilterStreamDemo { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("周杰伦"); list.add("王力宏"); list.add("陶喆"); list.add("林俊杰"); Stream<String> stream = list.stream().filter(element -> element.contains("王")); stream.forEach(System.out::println); } }
filter() 方法接收的是一个 Predicate(Java 8 新增的一个函数式接口,接受一个输入参数返回一个布尔值结果)类型的参数,所以,咱们能够直接将一个 Lambda 表达式传递给该方法,好比说 element -> element.contains("王") 就是筛选出带有“王”的字符串。
forEach() 方法接收的是一个 Consumer(Java 8 新增的一个函数式接口,接受一个输入参数而且无返回的操做)类型的参数,类名 :: 方法名是 Java 8 引入的新语法,System.out 返回 PrintStream 类,println 方法你应该知道是打印的。
stream.forEach(System.out::println); 至关于在 for 循环中打印,相似于下面的代码:
for (String s : strs) { System.out.println(s); }
很明显,一行代码看起来更简洁一些。来看一下程序的输出结果:
王力宏
2)映射
若是想经过某种操做把一个流中的元素转化成新的流中的元素,可使用 map() 方法。
public class MapStreamDemo { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("周杰伦"); list.add("王力宏"); list.add("陶喆"); list.add("林俊杰"); Stream<Integer> stream = list.stream().map(String::length); stream.forEach(System.out::println); } }
map() 方法接收的是一个 Function(Java 8 新增的一个函数式接口,接受一个输入参数 T,返回一个结果 R)类型的参数,此时参数 为 String 类的 length 方法,也就是把 Stream<String> 的流转成一个 Stream<Integer> 的流。
程序输出的结果以下所示:
3 3 2 3
3)匹配
Stream 类提供了三个方法可供进行元素匹配,它们分别是:
anyMatch(),只要有一个元素匹配传入的条件,就返回 true。
allMatch(),只有有一个元素不匹配传入的条件,就返回 false;若是所有匹配,则返回 true。
noneMatch(),只要有一个元素匹配传入的条件,就返回 false;若是所有匹配,则返回 true。
public class MatchStreamDemo { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("周杰伦"); list.add("王力宏"); list.add("陶喆"); list.add("林俊杰"); boolean anyMatchFlag = list.stream().anyMatch(element -> element.contains("王")); boolean allMatchFlag = list.stream().allMatch(element -> element.length() > 1); boolean noneMatchFlag = list.stream().noneMatch(element -> element.endsWith("沉")); System.out.println(anyMatchFlag); System.out.println(allMatchFlag); System.out.println(noneMatchFlag); } }
由于“王力宏”以“王”字开头,因此 anyMatchFlag 应该为 true;由于“周杰伦”、“王力宏”、“陶喆”、“林俊杰”的字符串长度都大于 1,因此 allMatchFlag 为 true;由于 4 个字符串结尾都不是“沉”,因此 noneMatchFlag 为 true。
程序输出的结果以下所示:
true true true
4)组合
reduce() 方法的主要做用是把 Stream 中的元素组合起来,它有两种用法:
- Optional<T> reduce(BinaryOperator<T> accumulator)
没有起始值,只有一个参数,就是运算规则,此时返回 Optional。
- T reduce(T identity, BinaryOperator<T> accumulator)
有起始值,有运算规则,两个参数,此时返回的类型和起始值类型一致。
来看下面这个例子。
public class ReduceStreamDemo { public static void main(String[] args) { Integer[] ints = {0, 1, 2, 3}; List<Integer> list = Arrays.asList(ints); Optional<Integer> optional = list.stream().reduce((a, b) -> a + b); Optional<Integer> optional1 = list.stream().reduce(Integer::sum); System.out.println(optional.orElse(0)); System.out.println(optional1.orElse(0)); int reduce = list.stream().reduce(6, (a, b) -> a + b); System.out.println(reduce); int reduce1 = list.stream().reduce(6, Integer::sum); System.out.println(reduce1); } }
运算规则能够是 Lambda 表达式(好比 (a, b) -> a + b),也能够是类名::方法名(好比 Integer::sum)。
程序运行的结果以下所示:
6 6 12 12
0、一、二、3 在没有起始值相加的时候结果为 6;有起始值 6 的时候结果为 12。
三、转换流0
既然能够把集合或者数组转成流,那么也应该有对应的方法,将流转换回去——collect() 方法就知足了这种需求。
public class CollectStreamDemo { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("周杰伦"); list.add("王力宏"); list.add("陶喆"); list.add("林俊杰"); String[] strArray = list.stream().toArray(String[]::new); System.out.println(Arrays.toString(strArray)); List<Integer> list1 = list.stream().map(String::length).collect(Collectors.toList()); List<String> list2 = list.stream().collect(Collectors.toCollection(ArrayList::new)); System.out.println(list1); System.out.println(list2); String str = list.stream().collect(Collectors.joining(", ")).toString(); System.out.println(str); } }
toArray() 方法能够将流转换成数组,你可能比较好奇的是 String[]::new,它是什么东东呢?来看一下 toArray() 方法的源码。
<A> A[] toArray(IntFunction<A[]> generator);
也就是说 String[]::new 是一个 IntFunction,一个能够产生所需的新数组的函数,能够经过反编译字节码看看它究竟是什么:
String[] strArray = (String[])list.stream().toArray((x$0) -> { return new String[x$0]; }); System.out.println(Arrays.toString(strArray));
也就是至关于返回了一个指定长度的字符串数组。
当咱们须要把一个集合按照某种规则转成另一个集合的时候,就能够配套使用 map() 方法和 collect() 方法。
List<Integer> list1 = list.stream().map(String::length).collect(Collectors.toList());
经过 stream() 方法建立集合的流后,再经过 map(String:length) 将其映射为字符串长度的一个新流,最后经过 collect() 方法将其转换成新的集合。
Collectors 是一个收集器的工具类,内置了一系列收集器实现,好比说 toList() 方法将元素收集到一个新的 java.util.List 中;好比说 toCollection() 方法将元素收集到一个新的 java.util.ArrayList 中;好比说 joining() 方法将元素收集到一个能够用分隔符指定的字符串中。
来看一下程序的输出结果:
[周杰伦, 王力宏, 陶喆, 林俊杰] [3, 3, 2, 3] [周杰伦, 王力宏, 陶喆, 林俊杰] 周杰伦, 王力宏, 陶喆, 林俊杰