java流之Stream
流:在现实中有移动,传播,延绵不绝, 其有难寻其源,难觅其踪,变约莫测等特点。
stream:是jdk8中新增的api成员,是对容器对象功能的增强,借助于同样是新出现的Lambda表达式,提供了方便、简洁、高效、链式等方式处理集合容器,以获取自己需要的结果。
java中的流stream与现实中的流同中有异,异中有同。相同之处是stream也是有移动,传播,延绵不绝(链式调用处理,一个函数处理完成后传递到下一个函数处理,直至自己需要的结果)。不同处stream有清晰确认的源头(集合),及清楚的知道了自己需要的结果。
stream流从业务逻辑说起、
数据的来源也许是广泛、复杂、无序的。而我们需要用到的数据往往不是直接取用,而是通过有条件的筛选、排序、计算、转化等操作后才获取到所需的数据。
假设有一个数字集合,我们需要获取大于5的值,且需要按大小来排序。如下集合。
List<Integer> numbers = Arrays.asList(1,4,3,2,6,5,8,11,9,15,10,12,7);
在没有stream流之前。使用jdk7的处理方式。
List<Integer> numbersGt5 = new ArrayList<Integer>(); //创建一个接收对象集合
for(Integer i : numbers) { //用Iterator更好,iterator只能单向的移动操作,不能做增,删等操作)。
if(i > 5) numbersGt5.add(i); //遍历对象,获取大于5的对象加入接收集合
}
Collections.sort(numbersGt5); //委托给Collections做排序操作。
jdk8引入Stream流之后的处理方式。
List<Integer> numbersGt5 = numbers.stream().filter(item -> item > 5).sorted().collect(Collectors.toList());
两种处理方式。从感观上讲,使用stream流处理数据,简洁明了,要比以前的处理方式少了很多代码。链式一个方法一个法衔接调用,根据方法名称和方法的注释也能很容易猜测调用的操作流程。最后返回处理后的集合。
其实处理数据的流程一般都是大同小异,stream流的特点是封装了很多实现的具体的实现细节,让你更好的专注了业务编写。Stream相当于一个管道,每调用一个方法就相当于有一个阀门做各种操作的处理过滤转换,最终返回需要的结果。还有一点,Stream流可以方便并行地处理业务数据,在大数据量的情况下带来了性能的提升,而只需要很方便的把numbers.stream() 替换为 numbers.parallelStream()即可,就可享受多核并行带来的快乐!
Stream的特点
- 流水式:很多方法返回的也是一个流(如fiter\map\storted返回的也是一个stream流),返回流可方便的将处理链式衔接起来,构成一个流水式的处理方式,方便做优化处理。
- 数据的内部迭代:调用的很多处理方法如filter\map\storted都会经历过一系列的内部的循环对比提供,但是他的内部数据的循环外部是无感知的。
- 一个流,内部只可迭代一次,迭代完之后流即关闭,即流只可消费一次。如下示例。
List<Integer> numbers = new ArrayList<> (Arrays.asList(1,4,3,2,6,5,8,11,9,15,10,12,7)); //创建集合
Stream<Integer> stream = numbers.stream(); //获取流
// stream.forEach(i -> System.out.println(i)); //第一次迭代了正常
// stream.forEach(i -> System.out.println(i)); //每二次迭代 报异常stream has already been operated upon or closed
stream.filter(item -> true);//(请先注释掉上面的循环forEach)过滤器亦然,内部也使用了迭代,也只可处理一次。
stream.filter(i -> i > 25);//每二次处理,报异常stream has already been operated upon or closed
Stream的操作分类
stream流的操作可将其分为两类
- filter、map、limit、sorted等方法操作后返回stream流对象。可以继续链式调用形成一条流水行执行。称为中间操作。
- collect等方法操作后,关闭流,执行可变还原操作。称为终端操作。
中间操作
中间操作给我们带来了两个特性。
- 延后触发。当我们执行终端操作时才会执行中间操作。
- 循环合并。多个需要循环操作的动作合并了为一个循环,更加的高效了。
我们从下面的代码来体验一下中间操作给我们带来的体验感
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Person {
private final int age;
private final String name;
private final Classify type;
public Person(int age, String name, Classify type) {
super();
this.age = age;
this.name = name;
this.type = type;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
public Classify getType() {
return type;
}
public enum Classify{
MAN,WOMAN;
}
@Override
public String toString() {
return "Person [age=" + age + ", name=" + name + ", type=" + type + "]";
}
public static void main(String[] args) {
List<Person> persons = Arrays.asList(new Person(10,"小明",Classify.MAN),new Person(32,"李丽",Classify.WOMAN),new Person(35,"张三",Classify.MAN),
new Person(18,"李雪",Classify.WOMAN),new Person(40,"杨华",Classify.WOMAN),new Person(21,"刘光",Classify.MAN),
new Person(60,"张菲",Classify.MAN),new Person(72,"刘老师",Classify.WOMAN),new Person(90,"王大力",Classify.MAN));
persons
.stream()
.filter(item ->{ // 过滤,获取男人且已成年的人员信息
System.out.println("operator filter method!"); //添加打印,确认是否有执行该操作
return item.getType() == Classify.MAN && item.getAge() > 18;
})
.map(item -> {
System.out.println("operator map method!");//添加打印,确认是否有执行该操作
return item.getName();
}) //获取名称
//.limit(1) //只取1个
//.collect(Collectors.toSet()) //终端操作,将流变为Set集合,并关闭流。
;
}
}
将我们的目光移动到man函数中,在这个代码中,我们执行2个小操作。
1、给 .collect(Collectors.toSet()) 添加注释//【//.collect(Collectors.toSet())】,运行该程序代码。从执行情况来看控制台,无任何的信息打印。说明了调用的filter\map等方法没有执行。
2、将//.collect(Collectors.toSet()) 的注释放开【.collect(Collectors.toSet())】,运行该程序代码,可能看到如下打印信息
operator filter method!
operator filter method!
operator filter method!
operator map method!
operator filter method!
operator filter method!
operator filter method!
operator map method!
operator filter method!
operator map method!
operator filter method!
operator filter method!
operator map method!
执行操作1,说明注释//.collect(Collectors.toSet()),没有执行调用的打印信息,表明了如果没有执行终端操作是不会触发中间操作。
执行操作2,放开.collect(Collectors.toSet())终端操作。打印了信息,与上面操作1相互印证了,中间操作具有延迟执行功能。
从打印的信息中看打印的顺序是不规范的。filter方法与map方法的参数交互替代互换打印,表明了只要前一个参数结果满足条件马上传递给参数传递给下一个继续处理,而不是一个中间操作执行完再执行下一个中间操作,是得到一个正确的结果马上传递给下一个中间操作处理,即本来需要多个循环才能完成的功能,合并到了一个循环。
执行操作3,放开.limit(1)的注释。再次查看打印信息如下。
operator filter method!
operator filter method!
operator filter method!
operator map method!
从打印信息上看,只打印了四次。从persions集合中看,第三个数据,则好满足了filter过滤条件性别男,大于18岁。把筛选出的第三个数据马上传递到下个中间操作map获取到名称,再马上传递到limit 操作,判断上已经满足了只取一个数据的条件。马上停止了循环。减少了不必要的操作,节省了效率,也再次映证了循环合并猜想,也引出了另一个功能短路。短路,满足了所有的条件之后马上退出循环。不再消费CPU。流的计算也是按需计算的。
终端操作
中间操作返回的都是stream对象。终端操作是从流水线中获取生成的结果。理论上所有返回不是stream对象的操作都是终端操作。比如foreach/count/collect方法都是终端操作。他是流水线最终的结果。
标签:java,Stream,stream,流之,filter,operator,操作,method,Classify From: https://www.cnblogs.com/crowing-bird/p/18686193