java8 出来了很久了。新特性 Lambda 表达式
和 Stream
在编码时,写起来“神采飞扬”。一句真香万岁。当然会用当然是前提,我们不能仅仅只是会用而已。用的更“骚气”才是重点。
首先,流是什么?流可以理解为集合类的迭代器,当然不是很明确。但可以这么理解,当然他们还有很多不同,可以参考官网文档。提供的API可以声明式的处理数据集合,组合Lambda表达式
简洁的处理数据,类似于排序不需要临时写实现。
@Data
@Builder
public static class Person{
/***
* 名称
*/
private String name;
/**
* 年龄
*/
private int age;
/**
* 爱好
*/
private List<Hobby> hobbies = Lists.newArrayList();
/**
* 爱好
*/
enum Hobby{
/**
* 打球
*/
BALL ,
/**
* 玩游戏
*/
GAME,
/**
* 跑步
*/
RUN,
/**
* 唱歌
*/
SINGING,;
}
}
List<Person> personList = Lists.newArrayList(
Person.builder().age(10).name("张三").hobbies(Lists.newArrayList(Person.Hobby.BALL, Person.Hobby.RUN)).build(),
Person.builder().age(11).name("李四").hobbies(Lists.newArrayList(Person.Hobby.GAME, Person.Hobby.RUN)).build(),
Person.builder().age(11).name("王五").hobbies(Lists.newArrayList(Person.Hobby.BALL, Person.Hobby.SINGING, Person.Hobby.GAME)).build(),
Person.builder().age(19).name("小明").hobbies(Lists.newArrayList(Person.Hobby.BALL, Person.Hobby.GAME, Person.Hobby.SINGING)).build(),
Person.builder().age(20).name("小鹏").hobbies(Lists.newArrayList(Person.Hobby.SINGING, Person.Hobby.GAME)).build());
比如我们需要将Person
集合按照年龄排序。临时实现
// 临时实现
personList.sort(new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge().compareTo(o2.getAge());
}
});
// java8 steam
personList = personList.stream().sorted(Comparator.comparing(Person::getAge)).collect(Collectors.toList());
对比一下,写法上有差别,但是比如再根据爱好过滤数据。在原来的方式里我们需要写个for循环过滤数据,而且还需要定义临时对象存储过滤后的对象。利用stream
personList = personList.stream()
.filter(person -> person.getHobbies().contains(Person.Hobby.GAME))
.sorted(Comparator.comparing(Person::getAge))
.collect(Collectors.toList());
再比如说,我们需要取前两个人的数据。原来的方式需要subList()
。而steam 仅需要加一个limit
即可,默默的说一句真香。
personList = personList.stream()
.filter(person -> person.getHobbies().contains(Person.Hobby.GAME))
.sorted(Comparator.comparing(Person::getAge))
.limit(2)
.collect(Collectors.toList());
如果有需求可以写一火车,换成原来的写法,可能就是一个灾难(当然不可能有这样的需求)。
通过这个例子我们可以理解操作流
- stream是一个流水线,可以组合我们的条件;(灵活)
- 省掉了我们很多实现,stream 利用了内部迭代,代替我们实现了很多工作; (简洁、易读)
- 我们把stream 替换成parallelStream
Returns a possibly parallel {@code Stream} with this collection as its source. It is allowable for this method to return a sequential stream.
还可以并行操作;(性能好)
总结&升华
我们可以把以上的集合操作理解为三部分。第一个就是转换成Stream。第二个就是中间操作,可以自由组合(当然不是随意组合。比如在过滤完数据,下一次操作要求过滤前的数据那是不可能的,再一个我们把集合类分组转换成map。下次操作必须要根据这个map的数据结构进行组合计算)。第三个就是collect 按照官方的解释是terminal operation
。
当一个terminal operation
操作结束后,我们可以说这个流已经被消费了。我们可以写代码测试一下。
Stream<Person> personStream = personList.parallelStream();
personStream.forEach(person -> {
System.out.println(JSON.toJSON(person));
});
personStream.forEach(person -> {
System.out.println(JSON.toJSON(person));
});
返回:
{"hobbies":["BALL","SINGING","GAME"],"name":"王五","age":11}
{"hobbies":["SINGING","GAME"],"name":"小鹏","age":20}
{"hobbies":["GAME","RUN"],"name":"李四","age":11}
{"hobbies":["BALL","RUN"],"name":"张三","age":10}
{"hobbies":["BALL","GAME","SINGING"],"name":"小明","age":19}
Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)
at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:583)
at com.navinfo.platform.common.StreamTest.main(StreamTest.java:100)
stream has already been operated upon or closed
流已经被消费了或者被关闭了,forEach
也是一个terminal operation
。 当我们多次执行的时候你可以看出。为什么异常可能在前面,我们的输出在后面。因为我们声明的是parallelStream
并行流。如果可以详细看到并行流的效果时 我们可以personList.parallelStream().forEach()
进行数据输出测试。从有序集合生成流时会保留原有的顺序,利用personList.stream().forEach()
进行数据输出测试。
总结
- 流支持一系列操作进行组合元素,类似于流水线;
- 流分为中间操作和
terminal operation
当有类型有forEach、count 、collect等terminal operation
可以理解为流已经被消费完了。流只能进行一次terminal operation
操作; - 流利用内部迭代:迭代通过filter、map、sorted等操作被抽象了(参考其他文章总结);
- 流中的元素是按需计算的(参考其他文章总结);
扩展
如果有些数据在内存分页,当然并非特殊需求也不会在内存中分页,我们可以利用stream提供的API进行分页操作。转换为Collcetion.stream()
进行一系列filter sorted 等等中间操作,然后对“结果流”进行分页操作,结合skip 、limit 中间操作实现内存分页操作。很简单脱离list.subList操作远离数组越界错误(IndexOutOfBoundsException
)