参考:
- https://github.com/Snailclimb/JavaGuide
- https://github.com/winterbe/java8-tutorial
- https://blog.csdn.net/y_k_y/article/details/84633001
- https://www.jianshu.com/p/461429a5edc9
简介
java.util.Stream
表示能应用在一组元素上一次执行的操作序列。Stream 操作分为中间操作或者最终操作两种,最终操作返回一特定类型的计算结果,而中间操作返回 Stream 本身,这样你就可以将多个操作依次串起来。Stream 的创建需要指定一个数据源,比如 java.util.Collection
的子类,List 或者 Set, Map 不支持。Stream 的操作可以串行执行或者并行执行。
它具有如下特点:
-
不是数据结构,不会保存数据。
-
一般不会修改原来的数据源,它会将操作后的数据保存到另外一个对象中。(peek方法可以修改流中元素)
-
惰性求值,流在中间处理过程中,只是对操作进行了记录,并不会立即执行,需要等到执行终止操作的时候才会进行实际的计算。
Stream 的创建
最常用的方式是通过 Collection.stream()
或者 Collection.parallelStream()
来创建一个 Stream。
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream(); // 创建顺序流
Stream<String> parallelStream = list.parallelStream(); // 创建并行流
使用 Arrays 中的 stream()
方法,可以将数组转成流。
Integer[] nums1 = new Integer[10]; // 注意使用Integer数组
Stream<Integer> stream = Arrays.stream(nums1);
int[] nums = new int[10]; // int数组只能生成 IntStream流
IntStream iantStrem = Arrays.stream(nums);
使用Stream中的静态方法:of()
、iterate()
、generate()
。
Stream<Integer> stream = Stream.of(1, 2, 3);
stream.forEach(System.out::println);
Stream<Integer> stream2 = Stream.iterate(0, x -> x + 2).limit(5); // limit 限制数量
stream2.forEach(System.out::println);
Stream<Integer> stream3 = Stream.generate(new Random()::nextInt).limit(5); // limit 限制数量
stream3.forEach(System.out::println);
使用 BufferedReader.lines()
方法,将每行内容转成流。
BufferedReader reader = new BufferedReader(new FileReader("a.txt"));
Stream<String> stream = reader.lines();
stream.forEach(System.out::println);
使用 Pattern.splitAsStream()
方法,将字符串分隔成流。
Pattern pattern = Pattern.compile(",");
Stream<String> stream = pattern.splitAsStream("a,bb,f,gh");
stream.forEach(System.out::println);
Stream方法分类
Stream方法可以分为中间操作(Intermediate Operations)和结束操作(Terminal Operations)。中间操作会返回新的Stream供后续操作,而结束操作会返回最终结果。
中间操作可以分析有状态(只有拿到所有元素之后才能继续)和无状态(元素处理不受之前影响)两类。
无状态操作包括:
- unordered()
- filter()
- map()
- mapToInt()
- mapToLong()
- mapToDouble()
- flatMap()
- flatMapToInt()
- flatMapToLong()
- flatMapToDouble()
- peek()
有状态操作包括:
- distict()
- sorted()
- limit()
- skip()
结束操作可以分为非短路操作(必须处理所有元素才能得到最终结果)和短路操作(遇到某些符合条件的元素即可得到最终结果)。
非短路操作保护:
- forEach()
- forEachOrdered()
- toArray()
- reduce()
- collect()
- max()
- min()
- count()
短路操作包括:
- anyMatch()
- allMatch()
- noneMatch()
- findFirst()
- findAny()
Stream 常用方法
本部分重点介绍一下 Stream 常用方法。
为了方便测试,先写个例子。
List<String> stringList = new ArrayList<>();
stringList.add("asc");
stringList.add("fff");
stringList.add("sxcfg");
stringList.add("ds");
stringList.add("aaa3");
filter
过滤通过一个 predicate 接口来过滤并只保留符合条件的元素,该操作属于中间操作,所以我们可以在过滤后的结果来应用其他Stream操作(比如forEach)。
stringList.stream().filter(s -> s.startsWith("a")).forEach(System.out::println);
sorted
排序是一个中间操作,返回的是排序好后的 Stream。如果你不指定一个自定义的 Comparator 则会使用默认排序。
stringList.stream().sorted().forEach(System.out::println);
需要注意的是,排序只创建了一个排列好后的Stream,而不会影响原有的数据源,如我们之前所说,Stream 一般不会修改数据源。
map
中间操作 map 会将元素根据指定的 Function 接口来依次将元素转成另外的对象。
stringList.stream().map(String::toUpperCase).forEach(System.out::println);
match
Stream 提供了多种匹配操作,允许检测指定的Predicate是否匹配整个Stream。所有的匹配操作都是最终操作 ,并返回一个 boolean 类型的值。
boolean anyStartsWithA = stringList.stream().anyMatch((s) -> s.startsWith("a")); // true
boolean allStartsWithA = stringList.stream().allMatch((s) -> s.startsWith("a")); // false
boolean nonStartsWithZ = stringList.stream().noneMatch((s) -> s.startsWith("z")); // true
count
计数是一个最终操作,返回 Stream 中元素的个数,需要注意返回值类型是 long。
long countStartsWithA = stringList.stream().filter((s) -> s.startsWith("a")).count();
这是一个最终操作 ,允许通过指定的函数来将stream中的多个元素规约为一个元素,规约后的结果是通过Optional 接口表示的。
Optional<String> reduced = stringList.stream()
.sorted()
.reduce((s1, s2) -> (s1 + "#" + s2));
reduced.ifPresent(System.out::println);
一般来说,字符串拼接、数值的 sum、min、max、average 都是特殊的 reduce。例如 Stream 的 sum 就相当于Integer sum = integers.reduce(0, (a, b) -> a+b);
,也有没有起始值的情况,这时会把 Stream 的前面两个元素组合起来,返回的是 Optional。
Parallel Stream
Stream有串行和并行两种,串行Stream上的操作是在一个线程中依次完成,而并行Stream则是在多个线程上同时执行。
并行Stream效率较高,但是不保证顺序。
IntStream、LongStream和DoubleStream
这三者分别是int、long和double类型的流,其实有很多和Stream类似的地方。
具体暂时先不展开介绍了。
网友评论