一 概述
数据渠道、管道,用于操作数据源(集合、数组等)所生成的元素序列。
集合讲的是数据,流讲的是计算
即一组用来处理数组,集合的API。
二 Stream特点
1 Stream 不是数据结构,没有内部存储,自己不会存储元素。
2 Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
3 Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
4 不支持索引访问。
5 延迟计算
6 支持并行
7 很容易生成数据或集合
8 支持过滤,查找,转换,汇总,聚合等操作。
三 应用场景
流式计算处理,需要延迟计算、更方便的并行计算
更灵活、简洁的集合处理方式场景
四 代码实现
4.1 运行机制
Stream分为源source,中间操作,终止操作。
流的源可以是一个数组,集合,生成器方法,I/O通道等等。
一个流可以有零个或多个中间操作,每一个中间操作都会返回一个新的流,供下一个操作使用,一个流只会有一个终止操作。
中间操作也称为转换算子-transformation
Stream只有遇到终止操作,它的数据源会开始执行遍历操作。
终止操作也称为动作算子-action
因为动作算子的返回值不再是 stream,所以这个计算就终止了
只有碰到动作算子的时候,才会真正的计算
4.2 创建流的方式
1 数组转换为流
String[] strings = { "q", "w", "e", "r" };
Stream<String> stream1 = Stream.of(strings);
2 通过集合
Arrays.asList : 把数组转换为集合
List<String> list = Arrays.asList(strings);
Stream<string> stream1 = list.stream();
3 通过Stream的generate创建
generate是一个无限流(无限大),所以经常结合limit一起使用,来限制最大个数
generate参数是一个 Supplier ,而 Supplier中有一个get方法用于获取数据, 而 get方法的返回值,会作为数据保存到这个流中,下面程序也就意味中该流中的数据都是1
Stream<Integer> stream2 = Stream.generate(()->1);
limit 是中间操作,设置流的最大个数,forEach 遍历,是终止操作
Stream<Integer> stream2 = Stream.generate(()->1);
stream2.limit(5).forEach(x->System.out.println(x) );
//运行结果为:1 1 1 1 1
4 通过String.iterate
String.iterate 也是无限流
Stream<Integer> stream3 = Stream.iterate(1, x->x+2);
表示 参数1表示起始值,x+2表示步长为2 ,相当于 for(int i =1 ; true ; i+=2)
Stream<Integer> stream3 = Stream.iterate(1, x->x+2);
stream3.limit(5).forEach(x->System.out.println(x));
//运行结果为:1 3 5 7 9
5 已有类的API
String string = "abc";
IntStream chars = string.chars();
chars.forEach(x->System.out.println((char)x));//a b c
4.3 常见的中间操作
一个中间操作链,对数据进行处理,一个流可以进行0~N个中间操作,他们每一个都生成新的流,方便下一个进行操作,但是只能有一个终止操作
filter: 对元素进行筛选,不符合就不要了
distinct: 去除重复元素
skip : 跳过多少元素
limit:取最大条数(前几条)
map: 对集合中元素进行遍历操作
sorted:排序
例子:
package day02;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Stream_01 {
public static void main(String[] args) {
List <String> list=Arrays.asList("a","b","c","a");
Stream<String> stream=list.stream();
// stream.filter(x->false);
// stream.filter(x->false);//流一旦用了之后,得重新生成
// stream has already been operated upon or closed(流已经被操作)
//可以通过链式调用解决,因为stream使用后必须生成新的流,不能使用原来的stream
// stream.filter(x->false).filter(x->false);//中间操作
//x->x.equals("a") 是一个断言操作,如果返回false就不要该数据,如果返回true就要该数据
List<String> value=stream.filter(x->x.equals("a")).collect(Collectors.toList());//转化为list
//collect(Collectors.toList()); 终止操作,把(Streams)流转化为集合的固定写法
System.out.println(value );//[a, a]
// value.forEach(x->System.out.println(x));
/**
* skip:跳过
*/
stream=list.stream();//重新生成新的流
value=stream.skip(2).collect(Collectors.toList());//跳过2个
value.forEach(x->System.out.println(x));//c a
/**
* map:对集合中的元素进行遍历并操作
*/
List<Integer> list2=Arrays.asList(1000,1200,1100,5000,900);
Stream <Integer> stream2=list2.stream();
//涨薪百分之十
List<Integer> result=stream2.map(x->x+x/10).collect(Collectors.toList());
System.out.println(result);//[1100, 1320, 1210, 5500, 990]
/**
* distinct: 去除重复
*/
stream=list.stream();
list=stream.distinct().collect(Collectors.toList());//[a, b, c]
System.out.println(list);
/**
* limit:取前几条
*/
stream=list.stream();
list=stream.limit(2).collect(Collectors.toList());
System.out.println(list);//[a, b]
/**
* sorted:排序
*/
stream2=list2.stream();
//涨薪百分之十
// result=stream2.sorted().collect(Collectors.toList());//默认升序
result=stream2.sorted((x,y)->y-x).collect(Collectors.toList());//自定义为降序
System.out.println(result);//[5000, 1200, 1100, 1000, 900]
}
}
运行时常见异常:
出现原因:流已经被操作了.流一旦用了之后,得重新生成
解决方法:重新生成流->Stream<String> stream=list.stream();
4.4 常见的终止操作
一旦执行终止操作 , 中间操作才会真正执行,并且stream就不能在使用了
forEach:遍历
collect:收集器
min ,max,count,agerage: 计算相关
anyMatch: 匹配数据,比如是否包含
package day02;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import javax.swing.plaf.ListUI;
public class Stream_03 {
public static void main(String[] args) {
// TODO Auto-generated method stub
List <String> list=Arrays.asList("a","b","c");
Stream<String> stream=list.stream();
/**
* forEach :遍历操作
*/
stream.forEach(x->System.out.println(x));//a,b,c
/**
* 计数
*/
stream=list.stream();
long count=stream.count();
System.out.println(count);//3
//所以这样很难体现出count的优势,一般需要结合中间操作执行,优势更大
stream=list.stream();
count=stream.filter(x->x.equals("a")).count();
System.out.println(count);//1
//获取最大值
List<Integer> list2=Arrays.asList(1,4,3,6,5);
Stream<Integer> stream2=list2.stream();
Integer i=stream2.max((x,y)->x-y).get();
System.out.println(i);//6
//获取最小值
list2=Arrays.asList(1,4,3,6,5);
stream2=list2.stream();
i=stream2.min((x,y)->x-y).get();
System.out.println(i);//1
//匹配器 anyMatch
stream2=list2.stream();
boolean flab=stream2.anyMatch(x->x==5);
System.out.println(flab);//true
System.out.println(list2.contains(5));//true
List<User> listu=new ArrayList<User>();
listu.add(new User(19, "张三"));
listu.add(new User(17, "张三"));
listu.add(new User(17, "张三"));
listu.add(new User(18, "张三"));
Stream<User> streamu=listu.stream();
flab=streamu.anyMatch(x->x.getAge()==19);
// 使用contains需要覆写equals方法
System.out.println("包含年龄19: "+flab); //包含年龄19: true
}
}
class User{
private int age;
public User(int age, String name) {
super();
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private String name;
}