Jav8中,在核心类库中引入了新的概念,流(Stream)。流使得程序媛们得以站在更高的抽象层次上对集合进行操作。
今天,居士将主要介绍Steam类中对应集合上操作的几个重要的方法。
1、 Steam举例
对使用Java的程序媛们,当需要处理集合里的每一个数据时,通常是使用迭代,再对每个返回的元素进行处理。比如:
int count = 0;
ArrayList nameList = new ArrayList<>();
nameList.add("仁昌居士");
nameList.add("仁昌居士");
nameList.add("痕无羽");
nameList.add("羽无痕");
for (String name: nameList) {
if(name.equals("仁昌居士"))
count++;
}
尽管这段代码思想上 并不难理解,但是存在几个问题:
(1) 从代码量上来看,每一次的循环集合类,都需要重复写很多的样板代码。
(2)对于for循环写的代码块,有些程序媛可能很难理解其编写意图。需要阅读整个循环体后,才能有一定的理解。假设只有一个for循环,相对理解并不难,但是当出现多层嵌套循环,那理解所花费的成本就大幅度提升了。
分析一下for循环的实现原理,可知是通过调用了iterator()方法,产生了一个Iterator对象,通过while方法遍历的显式调用这个对象的hasNext()和next()方法。以实现需求。这种遍历过程叫做外部遍历,是一种串行化操作。
Iterator iterator = nameList.iterator();
while(iterator.hasNext()){
String name = iterator.next();
if(name.equals("仁昌居士")){
count++;
}
}
注意事项:为什么对for循环叫他外部遍历而不是外部迭代的原因?可见另一篇文章:还未写,周末写。
相对于外部遍历,还有一种方法叫做内部遍历。通过内部遍历,将上述代码实现为:
long count = nameList.stream().filter(name -> name.equals("仁昌居士"))
.count();
上述代码实际是三步,第一步:nameList创建了一个Stream实例,第二步:用fliter操作符过滤找出为“仁昌居士”的name,并转换成另外一个Stream,第三步:把Stream的里面包含的内容按照某种算法来成型成一个值,代码中式用count操作符计算有几个这样的name。
2、 惰性求值和及早求值
通常,在Java中调有一个方法,计算机会随机执行相应的操作,比如通过println在终端上输出一条信息。Stream里的方法则有些不同。比如说:
nameList.stream().filter(name -> name.equals("仁昌居士"));
这行代码并没有通过fliter得到新的集合,只是对Stream进行了描述,这种方法叫做“惰性求值”方法,而之后的“.count()”使Stream产生了值的方法,叫做“及早求值”方法。
最好的验证方式就如下。
单纯的在filter中加入一条println语句:
nameList.stream()
.filter(name -> {
System.out.println(name);
return name.equals("仁昌居士");
});
运行结果是程序并没有输出对应信息。
再测试:在后面加入一个及早求值方法,如count(),