赞
踩
Stream是从某个数据源获得的支持聚合操作的元素序列。
名词解释:
Java8在推出流的同时,对集合框架也进行了一些比较大变更。主要是在Collection接口上提供了两种生成Stream的方法:
流的使用一般包括三件事:
操作 | 操作参数 | 函数描述符 |
---|---|---|
filter | Predicate | T -> boolean |
map | Function<T,R> | T->R |
limit | ||
sorted | Comparator | (T,T)->int |
distinct |
操作 | 目的 |
---|---|
forEach | 消费流中的每个元素并对其应用Lambda,这一操作返回void |
count | 返回流中元素的个数,这一操作返回long |
collect | 把流归约成一个集合,比如List、Map甚至是Integer |
reduce | 归约,统计 |
迭代流的每个元素。
dishList.stream().forEach(System.out::println);
对流中元素进行排序。
dishList.stream().sorted(Comparator.comparingInt(Dish::getCalories)).forEach(System.out::println);
过滤元素。
List<Dish> filter = dishList.stream().filter(Dish::isVegetarian).collect(Collectors.toList());
去除重复元素。
List<Integer> distinct = Arrays.asList(1, 5, 6, 5, 8, 1, 9, 10).stream().distinct().collect(Collectors.toList());
限制返回元素个数。
List<Integer> limit = Arrays.asList(1, 5, 6, 5, 8, 1, 9, 10).stream().distinct().limit(3).collect(Collectors.toList());
跳过前面的元素个数,与limit联合使用可以实现类似sql的分页功能。
List<Integer> skip = Arrays.asList(1, 5, 6, 5, 8, 1, 9, 10).stream().distinct().skip(2).limit(3).collect(Collectors.toList());
可以将流转换成另外一个元素的流。
List<String> map = dishList.stream().map(Dish::getName).collect(Collectors.toList());
流的扁平化(将流中的流扁平化为一个流)。
List<String> flatMap = dishList.stream().flatMap(d -> Arrays.stream(d.getName().split(""))).distinct().collect(Collectors.toList());
只要有一个元素匹配就返回true。
boolean anyMatch = Arrays.asList(1, 5, 6, 5, 8, 1, 9, 10).stream().anyMatch(i -> i % 2 == 0);
所有元素匹配返回true。
boolean allMatch = Arrays.asList(1, 5, 6, 5, 8, 1, 9, 10).stream().allMatch(i -> i % 2 == 0);
找到任意一个元素就返回。
Arrays.asList(1, 5, 6, 5, 8, 1, 9, 10).stream().findAny().ifPresent(System.out::println);
findAny()操作,返回的元素是不确定的,对于同一个列表多次调用findAny()有可能会返回不同的值。使用findAny()是为了更高效的性能。如果是数据较少,串行地情况下,一般会返回第一个结果,如果是并行的情况,那就不能确保是第一个。
返回第一个元素。
Arrays.asList(1, 5, 6, 5, 8, 1, 9, 10).stream().findFirst().ifPresent(System.out::println);
对所有的元素求和。
Optional.ofNullable(dishList.stream().map(Dish::getCalories).reduce(0, Integer::sum)).ifPresent(System.out::println); // 等价于下面的,注意返回值不一样
dishList.stream().map(Dish::getCalories).reduce(Integer::sum).ifPresent(System.out::println);
对所有的元素求最大值。
dishList.stream().map(Dish::getCalories).reduce(Integer::max).ifPresent(System.out::println);
对所有的元素求最小值。
dishList.stream().map(Dish::getCalories).reduce(Integer::min).ifPresent(System.out::println);
<U> U reduce(U identity,
BiFunction<U, ? super T, U> accumulator,
BinaryOperator<U> combiner);
参数解释:
第三个参数用于在并行计算下合并各个线程的计算结果,并行流运行时,内部使用了fork-join框架。
多线程时,多个线程同时参与运算,多个线程执行任务,必然会产生多个结果,那么如何将他们进行正确的合并,这就是第三个参数的作用。
package com.morris.java8.stream;
import java.util.stream.IntStream;
public class ReduceExample {
public static void main(String[] args) {
Integer sum = IntStream.rangeClosed(1, 1000).boxed().reduce(0, Integer::sum, (x, y) -> {
// 不会执行,不影响结果
return 0;
});
System.out.println("sum=" + sum);
System.out.println("-------------");
Integer sum2 = IntStream.rangeClosed(1, 1000).boxed().parallel().reduce(0, Integer::sum, (x, y) -> {
System.out.print("thread name: " + Thread.currentThread().getName());
System.out.print(" x=" + x);
System.out.println(" y=" + y);
return x + y;
});
System.out.println("sum2=" + sum2);
}
}
运行结果如下:
sum=500500
-------------
thread name: main x=40703 y=45297
thread name: main x=32953 y=37422
thread name: main x=70375 y=86000
thread name: main x=56203 y=61047
thread name: main x=48453 y=53172
thread name: main x=101625 y=117250
thread name: main x=156375 y=218875
thread name: ForkJoinPool.commonPool-worker-2 x=17453 y=21672
thread name: ForkJoinPool.commonPool-worker-1thread name: ForkJoinPool.commonPool-worker-2 x=9703 x=25203 y=13797
y=29547
thread name: ForkJoinPool.commonPool-worker-2 x=39125 y=54750
thread name: ForkJoinPool.commonPool-worker-1 x=1953 y=5922
thread name: ForkJoinPool.commonPool-worker-1 x=7875 y=23500
thread name: ForkJoinPool.commonPool-worker-1 x=31375 y=93875
thread name: ForkJoinPool.commonPool-worker-1 x=125250 y=375250
sum2=500500
从运行结果可知,reduce方法的第三个参数用于在并行计算下合并各个线程的计算结果。
在Stream里元素都是对象,那么,当我们操作一个数字流的时候就不得不考虑一个问题,拆箱和装箱。虽然自动拆箱不需要我们处理,但依旧有隐含的成本在里面。
Java8引入了3个原始类型特化流接口来解决这个问题:IntStream,DoubleStream,LongStream, 分别将流中的元素特化为int、long、double,从而避免了暗含的装箱成本。
将对象流转换为数值流的常用方法是mapToInt、mapToDouble和mapToLong。
IntStream intStream = Arrays.asList(1, 3, 4, 6, 9).stream().mapToInt(Integer::intValue);
使用boxed方法。
Stream<Integer> boxed = intStream.boxed();
有时候需要生成一个数值范围,比如1到30. 可以直接使用数值流生成。
IntStream.range(1, 5).forEach(System.out::print); // 不包含结束值
System.out.println();
IntStream.rangeClosed(1, 5).forEach(System.out::print); // 包含结束值
System.out.println();
// min
IntStream.rangeClosed(1, 5).min().ifPresent(System.out::println);
// max
IntStream.rangeClosed(1, 5).max().ifPresent(System.out::println);
// sum
System.out.println(IntStream.rangeClosed(1, 5).sum());
// summaryStatistics
System.out.println(IntStream.rangeClosed(1, 5).summaryStatistics());
Optional可以用Integer、String等参考类型来参数化。对于三种数据流,也分别有一个Optional原始类型:OptionalInt、OptionalDouble和OptionalLong。
IntStream.rangeClosed(1, 5).min()
有可能没有最小值,这时返回的是一个OptionalInt,返回0就不合理了。
Optional.ofNullable(Stream.of("hello", "morris", "world", "stream").collect(Collectors.joining(","))).ifPresent(System.out::println);
Optional.ofNullable(Arrays.stream(new int[]{2, 3, 1, 4}).boxed().map(i -> String.valueOf(i)).collect(Collectors.joining(","))).ifPresent(System.out::println);
try(Stream<String> stream = Files.lines(Paths.get("D:\\gitPrj\\morris-book\\Java\\rocketmq\\java8\\src\\main\\java\\com\\morris\\java8\\stream\\Trader.java"))) {
stream.limit(5).forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
使用无限流时注意使用limit限制流的大小,否则会一直无限生成下去。
// iterate生成流
Optional.ofNullable(Stream.iterate(0, n -> n + 2).limit(10).map(i -> String.valueOf(i)).collect(Collectors.joining(","))).ifPresent(System.out::println);
// generate生成流
Optional.ofNullable(Stream.generate(Math::random).limit(5).map(d -> String.valueOf(d)).collect(Collectors.joining(","))).ifPresent(System.out::println);
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。