本文转载于陈争云,占宇剑和司磊的《Java8中的Streams API详解》一文。
- 归类
-
Intermediate 操作
一个流可以后面跟随零个或多个intermediate操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。map (mapToInt, 等) :对集合中的每个元素操作,返回一个值,1对1的
flatMap :对集合中的每个元素操作,一个元素可以返回多个结果,1对多
filter:过滤,返回符合条件的
distinct、
sorted:排序
peek、
limit、
skip、
parallel:并行执行
sequential、
unordered -
Terminal 操作
一个流只能有一个terminal操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以,这必定是流的最后一个操作。Terminal操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个side effect。
forEach:循环
forEachOrdered、
toArray、
reduce:有初始值 a,a和第一个元素操作结果a1,结果a1和第二个元素操作结果a12,依次类推;
collect:收集元素 collect(Collectors.toList())
min、
max、
count、
anyMatch、
allMatch、
noneMatch、
findFirst、
findAny、
iterator -
Short-circuiting 操作
还有一种操作被称为short-circuiting。用以指:对于一个intermediate操作,如果它接受的是一个无限大(infinite/unbounded)的Stream,但返回一个有限的新Stream;对于一个terminal操作,如果它接受的是一个无限大的Stream,但能在有限的时间计算出结果。
当操作一个无限大的 Stream,而又希望在有限时间内完成操作,则在管道内拥有一个short-circuiting操作是必要非充分条件。
在对一个Stream进行多次转换操作(Intermediate 操作),每次都对Stream的每个元素进行转换,而且是执行多次,这样时间复杂度就是N(转换次数)个for循环里把所有操作都做掉的总和吗?其实不是这样的,转换操作都是lazy的,多个转换操作只会在Terminal操作的时候融合起来,一次循环完成。我们可以这样简单的理解,Stream里有个操作函数的集合,每次转换操作就是把转换函数放入这个集合中,在Terminal 操作的时候循环Stream对应的集合,然后对每个元素执行所有的函数。
@RunWith(JUnit4.class)
public class DemoApplicationTests {
private List<DataBean> totalStocks = new ArrayList<>();
@Before
public void b(){
DataBean stock1 = new DataBean();
stock1.setDeptId(2);
stock1.setType(2);
stock1.setNum(2);
totalStocks.add(stock1);
DataBean stock2 = new DataBean();
stock2.setDeptId(2);
stock2.setType(2);
stock2.setNum(3);
totalStocks.add(stock2);
DataBean stock3 = new DataBean();
stock3.setDeptId(3);
stock3.setType(3);
stock3.setNum(5);
totalStocks.add(stock3);
DataBean stock4 = new DataBean();
stock4.setDeptId(3);
stock4.setType(3);
stock4.setNum(4);
totalStocks.add(stock4);
DataBean stock5 = new DataBean();
stock5.setDeptId(4);
stock5.setType(4);
stock5.setNum(10);
totalStocks.add(stock5);
}
//groupingBy一个参数的:参数就是分组的key; 按key( e.getDeptId()+":"+e.getType() )分组,返回值 Map<String, List<DataBean>>
@Test
public void groupingBy1() {
Optional.ofNullable( totalStocks.stream().collect(Collectors.groupingBy(e->e.getDeptId()+":"+e.getType()) ))
.ifPresent(System.out::println);
}
//groupingBy两个参数的:第一个参数就是分组的key,第二个参数是分组后计算
@Test
public void groupingBy2() {
Optional.ofNullable( totalStocks.stream().collect(Collectors.groupingBy(e->e.getDeptId()+":"+e.getType(),Collectors.averagingInt(DataBean::getNum) ) ))
.ifPresent(System.out::println);
}
//平均值, int long double
@Test
public void testAveraging(){
Optional.ofNullable(totalStocks.stream().collect(Collectors.averagingInt(DataBean::getNum)))
.ifPresent(System.out::println);
}
//collectingAndThen(收集方法,对收集方法的结果进一步处理)
@Test
public void testcollectingAndThen(){
Optional.ofNullable(totalStocks.stream().collect(Collectors.collectingAndThen(Collectors.averagingInt(DataBean::getNum), e->e+"=平均数" ) ) )
.ifPresent(System.out::println);
}
//.count() == .collect(Collectors.counting() )
@Test
public void testcounting(){
Optional.ofNullable(totalStocks.stream().count())
.ifPresent(System.out::println);
}
}