- 这一篇内容要讲的是流,关于流的概念,流的内部迭代跟外部迭代的区别之类的偏概念性的问题,下一篇在细讲流到底是什么用
- 那么说到流,我们自然就会想到水,这就涉及到水源头是哪里,水怎么流过来的,水会被我们的桶收集起来,还是说用水杯收集起来,那么问题就一下子出现了三个
- 流的源头是哪里
- 流的源头包括集合,数组或者一些输入输出资源,但是需要注意的是,从有序集合生成流时会保持原有的数据,从列表生成的流,其元素数据与列表一致
- 流是怎么个处理过程
- 这其实就要设计到用到了具体的api了,下面会说到
- 流的收集
- 无非就是收集成集合等一些等存储数据的数据结构中
- 流和集合作对比
- 一点很清楚的就是,流是来做计算的,而集合是用来存储的,这个应该很容易理解
- 还有一个就是:我们需要看一部电影,那么无非就是两种在线看和下载下来看,那么在线看就属于流模式,而整个资源下载下来看就属于集合模式了,所以从简单的说集合与流之间的差异就在与什么时候计算。集合是一个内存中的数据结构,它包含了所有值,就比如我们使用的list,不管添加还是删除他总是在内存中操作的,并且它是每个元素先计算出来再添加到集合的,而流呢?他就像我们在线看电影一样,不需加载全部的数据,剧情无聊快进的时候,流的处理方式就是加载你点击的时间点之后的几帧来供你观看,这就是他们的区别
- 好了扯了一大堆概念,让我们来看看集合和流处理数据的方式
- 我们利用下面这个类的结构来编写代码
@Data
public class Dish {
private final String name;
private final boolean vegetarian; //是否是素菜
private final int calories; //卡路里
private final Type type;
private enum Type{
MEAT,FISH,OTHER;
}
public Dish(String name, boolean vegetarian, int calories, Type type) {
this.name = name;
this.vegetarian = vegetarian;
this.calories = calories;
this.type = type;
}
}
- 好了让我们创建一个集合
List<Dish> menu = Arrays.asList(
new Dish("apple",true,50, Dish.Type.OTHER),
new Dish("chicken",false,350, Dish.Type.MEAT),
new Dish("rich",true,150, Dish.Type.OTHER),
new Dish("pizza",true,350, Dish.Type.OTHER),
new Dish("fish",false,250, Dish.Type.FISH),
new Dish("orange",true,70, Dish.Type.OTHER),
new Dish("banana",true,60, Dish.Type.OTHER));
- 集合操作:找出卡路里最低排名的前三位
@Test
public void test() throws Exception {
menu.sort(new Comparator<Dish>() {
@Override
public int compare(Dish o1, Dish o2) {
return o1.getCalories() - o2.getCalories();
}
});
for (int i = 0; i < 3; i++) {
System.out.println(menu.get(i));
}
}
- 流操作:找出卡路里最低排名的前三位
@Test
public void test() throws Exception {
List<Dish> collect = menu
.stream()
.sorted(Comparator.comparing(Dish::getCalories))
.limit(3)
.collect(Collectors.toList());
for (Dish dish : collect) {
System.out.println(dish);
}
}
- 观察上面两种,肯定是流操作比较好一些,因为它操作的每一步都很清楚的告诉开发者他的流在干什么,如上就是流和集合的一个简单对比
- 如上其实也反映了两个:内部迭代和外部迭代,流就属于内部迭代,集合的操作的方法就是外部迭代
- 内部迭代就是按照我们的意思,类似SQL似的查询操作,比如取前三啊那就是limit(3),然后他会根据你的查询条件自动的去遍历你的集合,这样就省去自己去一步步的写逻辑了
- 外部迭代自己理解的就是需要开发者手动编写遍历逻辑
- 那么流是一个一次性的,所以我们看如下
Stream<Dish> limit = menu.stream()
.sorted(Comparator.comparing(Dish::getCalories))
.limit(3);
List<Dish> collect1 = limit.collect(Collectors.toList());
//IllegalStateException: stream has already been operated upon or closed
List<Dish> collect2 = limit.collect(Collectors.toList());
- 上面就充分说明了,流只能遍历一次。当遍历一次之后这个流就被消费掉了,如果重新需要,那么需要从源头那再获取个stream
- 我们看下面的代码
Stream<Dish> limit = menu.stream()
.sorted(Comparator.comparing(Dish::getCalories))
.limit(3);
System.out.println(limit);//java.util.stream.SliceOps$1@436e852b
List<Dish> collect1 = limit.collect(Collectors.toList()); //结果
- 这段代码就可以说明了,流是一种类似懒加载模式的,只有触发终端操作,那么这个流才会开始启动遍历数据,如上我们一直到limit(3)这都是流的中间操作,流的中间操作是不会产生数据的,只有出发了collect方法这个终端方法才会开始流的遍历工作,当然中间操作不止limit,sorted,终端操作也不止collect,这一篇只是简单的引入一下流而已,下一篇内容将会讲stream的使用
好了这一篇内容就介绍到这,主要介绍了流的概念,流和集合的区别,内外迭代和流的中间操作以及终端操作,还有重要的一点就是流是一次性的