Stream
简介
Stream(流)是数据渠道,用于操作数据源(集合、数组等),生成元素序列。换言之,集合是存储数据的容器,流使用操作这些数据的
Stream可以对集合进行非常复杂的查找、过滤、映射数据等操作,类似于SQL执行数据库查询。Stream提供了一种高效且易于使用的处理数据的方式
注意:
- Stream不会存储数据
- Stream不会改变源数据,通过一系列操作数据源会返回一个持有结果的新Stream
- Stream操作是延迟执行的,意味着流会等到需要结果的时候才执行
执行步骤
- 创建Stream:通过数据源(集合、数组等)获取一个Stream
- 中间操作:中间操作链,对源数据的数据进行处理
- 终止操作:执行中间操作,并产生结果
创建Stream
public class Test1 {
@Test
public void test01() {
//方式一:通过Collection接口提供的stream()-串行流或parallelStream()-并行流 获取流对象
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
//方式二:通过Arrays的静态方法stream()获取流对象
String[] strs = new String[10];
Stream<String> stream2 = Arrays.stream(strs);
//方式三:通过Stream的静态方法of()获取流对象
Stream<String> stream3 = Stream.of("aaa","bbb","ccc");
//方式四:创建无限流
//iterate()迭代
Stream<Integer> stream4 = Stream.iterate(1, (x)->x+=100);
stream4.limit(3).forEach(System.out::println);
//方式五:创建无限流
//generate()生成
Stream<Double> stream5 = Stream.generate(()->Math.random());
stream5.limit(3).forEach(System.out::println);
}
}
注意:多个中间操作可以连接成一个流水线,除非流水线触发终止操作,否则中间操作不会执行任何的处理,而在终止操作时一次性全部处理,称为惰性求值/延迟加载
中间操作 - 筛选与切片
方法 | 描述 |
---|---|
filter(Predicate p) | 从流中排除元素 |
limit(long maxSize) | 设置限制数据条数 |
skip(long n) | 跳过元素,返回跳过n个元素的流,若流中不满足n个元素则返回空流。与limit()互补 |
distinct() | 筛选,流通过元素对象的hashCode()和equals()方法去除重复元素 |
如果没有终止操作,中间操作就不会被调用,终止操作时一次性全部处理,这种称为惰性求值/延迟加载
public class Test1 {
List<Student> stuList = Arrays.asList(
new Student("张三", 28, 4800,Course.JAVA),
new Student("李四", 36, 7200,Course.JAVA),
new Student("王五", 19, 9600,Course.HTML),
new Student("赵六", 42, 6100,Course.HTML),
new Student("孙七", 23, 9600,Course.PYTHON),
new Student("吴八", 31, 3000,Course.PYTHON),
new Student("李四", 36, 7200,Course.JAVA));
@Test
public void test01() {
//需求1:过滤掉小于5000的学生对象
Stream<Student> stream = stuList.stream().filter((x)-> {
System.out.println("中间操作");
return x.getSalary()>5000;
});
//迭代输出流里的数据就等同于终止操作
//迭代功能在forEach()中完成,称为内部迭代(集合使用iterator()称为外部迭代)
stream.forEach(System.out::println);
}
@Test
public void test02() {
//需求2:过滤掉小于5000的学生对象,并显示3条
//注意:因为限制了数据条数,所以满足数据条数后,后续的操作就不再运行了,效率就提高了
Stream<Student> stream = stuList.stream().filter((x)-> {
System.out.println("短路");
return x.getSalary()>5000;
}).limit(3);
//迭代输出流里的数据就等同于终止操作
//迭代功能在forEach()中完成,称为内部迭代(集合使用iterator()称为外部迭代)
stream.forEach(System.out::println);
}
@Test
public void test03() {
//需求3:过滤掉小于5000的学生对象,并跳过第1个学生对象
Stream<Student> stream = stuList.stream().
filter((x)-> x.getSalary()>5000).
skip(1);
//迭代输出流里的数据就等同于终止操作
//迭代功能在forEach()中完成,称为内部迭代(集合使用iterator()称为外部迭代)
stream.forEach(System.out::println);
}
@Test
public void test04() {
//需求4:过滤掉小于5000的学生对象,并筛选掉重复元素
//Stream底层通过元素对象(Student对象)的hashCode()和equals()方法去除重复元素
Stream<Student> stream = stuList.stream().
filter((x)-> x.getSalary()>5000).
distinct();
//迭代输出流里的数据就等同于终止操作
//迭代功能在forEach()中完成,称为内部迭代(集合使用iterator()称为外部迭代)
stream.forEach(System.out::println);
}
}
enum Course{//课程枚举
JAVA,HTML,PYTHON;
}
class Student{//学生类
private String name;
private int age;
private double salary;
private Course course;
...
}
中间操作 - 映射
方法 | 描述 |
---|---|
map(Function<?, ? > mapper) | 将流中所有元素映射成一个新的元素或者提取信息 |
flatMap(Function<?, ? extends Stream<? >> mapper) | 将流中的流整合(整合成平面/平铺流) |
public class Test1 {
List<String> nameList = Arrays.asList("张三","李四","王五","赵六","孙七","吴八");
List<Student> stuList = Arrays.asList(
new Student("张三", 28, 4800,Course.JAVA),
new Student("李四", 36, 7200,Course.JAVA),
new Student("王五", 19, 9600,Course.HTML),
new Student("赵六", 42, 6100,Course.HTML),
new Student("孙七", 23, 9600,Course.PYTHON),
new Student("吴八", 31, 3000,Course.PYTHON),
new Student("李四", 36, 7200,Course.JAVA));
@Test
public void test01() {
//map() - 将流中所有元素映射成一个新的元素 或者 提取信息
//方式1:映射成一个新的元素
//需求:将流中所有字符串拼接成新的字符串
nameList.stream().map((str)-> str.charAt(0)).forEach(System.out::println);
//方式2:映射成提取信息
//需求:把原来流中的学生对象替换成学生姓名
stuList.stream().map((stu)-> stu.getName()).forEach(System.out::println);
}
@Test
public void test02() {
//带着需求学flatMap()
//flatMap() - 将流中的流整合(整合成平面/平铺流)
//需求:将nameList里的字符串转换为字符输出
//解决方案1:使用map()完成需求,可以看到流里包含另外的流,非常麻烦
Stream<Stream<Character>> stream = nameList.stream().
map(Test1::getCharacterStream);//{{'张','三'},{'李','四'},...}
stream.forEach((sm) -> {
sm.forEach(System.out::println);
});
//解决方案2:使用flatMap()完成需求,将流中的流一并整合
nameList.stream().flatMap((str)-> getCharacterStream(str)).
forEach(System.out::println);//{'张','三'},{'李','四'},...
}
//将字符串拆分出字符转换为流的方法
public static Stream<Character> getCharacterStream(String str){//"张三"
ArrayList<Character> list = new ArrayList<>();//'张','三'
for (Character c : str.toCharArray()) {
list.add(c);
}
return list.stream();
}
}
enum Course{//课程枚举
JAVA,HTML,PYTHON;
}
class Student{//学生类
private String name;
private int age;
private double salary;
private Course course;
...
}
中间操作 - 排序
方法 | 解释 |
---|---|
sorted() | 使用元素原有排序规则 - Comparable |
sorted(Comparator<? super T> comparator) | 使用自定义排序规则 - Comparator |
public class Test1 {
List<Student> stuList = Arrays.asList(
new Student("张三", 28, 4800,Course.JAVA),
new Student("李四", 36, 7200,Course.JAVA),
new Student("王五", 19, 9600,Course.HTML),
new Student("赵六", 42, 6100,Course.HTML),
new Student("孙七", 23, 9600,Course.PYTHON),
new Student("吴八", 31, 3000,Course.PYTHON),
new Student("吴八", 31, 3000,Course.PYTHON));
@Test
public void test01() {
//使用元素原有排序规则(Comparable<T>)
//需求:按照年龄排序
stuList.stream().sorted().forEach(System.out::println);
}
@Test
public void test02() {
//使用自定义排序规则(Comparator<T>)
//需求:按照工资排序
stuList.stream().sorted((e1,e2)->{
if(e1.getSalary() == e2.getSalary()){
return 1;
}
return (int)(e1.getSalary() - e2.getSalary());
}).forEach(System.out::println);
}
}
enum Course{//课程枚举
JAVA,HTML,PYTHON;
}
class Student implements Comparable<Student>{//学生类
private String name;
private int age;
private double salary;
...
}
终止操作 - 匹配与查找
方法 | 描述 |
---|---|
allMatch(Predicate<? super T> predicate) | 检查是否匹配所有元素 |
anyMatch(Predicate<? super T> predicate) | 检查是否匹配至少一个元素 |
noneMatch(Predicate<? super T> predicate) | 检查是否没有匹配所有元素 |
findFirst() | 返回第一个元素 |
findAny() | 返回任意一个元素(但效果不好) |
count() | 返回流中元素的个数 |
max(Comparator<? super T> comparator) | 返回流中最大值 |
min(Comparator<? super T> comparator) | 返回流中最小值 |
public class Test1 {
List<Student> stuList = Arrays.asList(
new Student("张三", 28, 4800,Course.JAVA),
new Student("李四", 36, 7200,Course.JAVA),
new Student("王五", 19, 9600,Course.HTML),
new Student("赵六", 42, 6100,Course.HTML),
new Student("孙七", 23, 9600,Course.PYTHON),
new Student("吴八", 31, 3000,Course.PYTHON),
new Student("吴八", 31, 3000,Course.PYTHON));
@Test
public void test01() {
//需求1:检查流中所有元素是否匹配 工资>5000
boolean allMatch = stuList.stream().allMatch((stu) -> stu.getSalary()>5000);
System.out.println(allMatch);//false
//需求2:检查流中所有元素至少匹配一个 工资>5000
boolean anyMatch = stuList.stream().anyMatch((stu) -> stu.getSalary()>5000);
System.out.println(anyMatch);//true
//需求3:检查流中所有元素是否没有匹配 工资>5000
boolean noneMatch = stuList.stream().noneMatch((stu) ->
stu.getSalary()>5000);
System.out.println(noneMatch);
//需求4:返回工资最高的学生信息
Optional<Student> findFirst = stuList.stream().
sorted((stu1,stu2)->Double.compare(
stu1.getSalary(),stu2.getSalary())).
findFirst();
Student stu = findFirst.get();
//这种写法防止NullPointerException出现
//Student stu = findFirst.orElse(new Student());
System.out.println(stu);
//需求5:返回随机学生信息(但效果不好)
Optional<Student> findAny = stuList.stream().findAny();
Student stu = findAny.get();
System.out.println(stu);
//需求6:获取学生个数
long count = stuList.stream().count();
System.out.println(count);
//需求7:获取最高工资的学生信息
Optional<Student> max = stuList.stream().
max((stu1,stu2)->Double.compare(stu1.getSalary(),stu2.getSalary()));
System.out.println(max.get());
//需求8:获取最低工资的学生信息
Optional<Student> min = stuList.stream().
min((stu1,stu2)->Double.compare(stu1.getSalary(),stu2.getSalary()));
System.out.println(min.get());
}
}
enum Course{//课程枚举
JAVA,HTML,PYTHON;
}
class Student implements Comparable<Student>{//学生类
private String name;
private int age;
private double salary;
private Course course;
...
}
终止操作 - 归约
归约:将流中的元素反复结合起来,得到一个值
map+reduce的连接通常称为map_reduce模式,因Google用它进行网络搜索而出名
方法 | 描述 |
---|---|
reduce( T identity , BinaryOperator accumulator) | 参数:(初始值,结合逻辑) |
reduce(BinaryOperator accumulator) | 参数:(结合逻辑) |
public class Test1 {
List<Integer> numList = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
List<Student> stuList = Arrays.asList(
new Student("张三", 28, 4800,Course.JAVA),
new Student("李四", 36, 7200,Course.JAVA),
new Student("王五", 19, 9600,Course.HTML),
new Student("赵六", 42, 6100,Course.HTML),
new Student("孙七", 23, 9600,Course.PYTHON),
new Student("吴八", 31, 3000,Course.PYTHON),
new Student("李四", 36, 7200,Course.JAVA));
@Test
public void test01() {
//需求:获取numList集合中元素的总和
Integer reduce = numList.stream().
reduce(0, (x,y)->x+y);
System.out.println(reduce);
}
@Test
public void test02() {
//需求:获取stuList集合中所有学生工资总和
Optional<Double> reduce = stuList.stream().
map(Student::getSalary).reduce(Double::sum);
Double sumSalary = reduce.get();
System.out.println(sumSalary);
}
}
enum Course{//课程枚举
JAVA,HTML,PYTHON;
}
class Student implements Comparable<Student>{//学生类
private String name;
private int age;
private double salary;
private Course course;
终止操作 - 收集
收集:将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法
方法 | 描述 |
---|---|
collect(Collector<? super T, A, R> collector) | 把元素放入Collector集合中 |
public class Test1 {
List<Student> stuList = Arrays.asList(
new Student("张三", 28, 4800,Course.JAVA),
new Student("李四", 36, 7200,Course.JAVA),
new Student("王五", 19, 9600,Course.HTML),
new Student("赵六", 42, 6100,Course.HTML),
new Student("孙七", 23, 9600,Course.PYTHON),
new Student("吴八", 31, 3000,Course.PYTHON),
new Student("李四", 36, 7200,Course.JAVA));
@Test
public void test01() {
//把数据收集到集合中
//需求1:把当前学生姓名提取出来,并把数据放入List集合中
List<String> list = stuList.stream().
map(Student::getName).
collect(Collectors.toList());
list.forEach(System.out::println);
//需求2:把当前学生姓名提取出来,并把数据放入Set集合中
Set<String> set = stuList.stream().
map(Student::getName).collect(Collectors.toSet());
set.forEach(System.out::println);
//需求3:把当前学生姓名提取出来,并把数据放入指定集合中
HashSet<String> hashSet = stuList.stream().
map(Student::getName).collect(Collectors.toCollection(HashSet::new));
hashSet.forEach(System.out::println);
}
@Test
public void test02() {
//收集流中的各种数据
//需求1:收集/获取学生个数
Long count = stuList.stream().
map(Student::getName).collect(Collectors.counting());
System.out.println(count);
//需求2:收集/获取学生平均工资
Double avg = stuList.stream().
collect(Collectors.averagingDouble(Student::getSalary));
System.out.println(avg);
//需求3:收集/获取学生总工资
Double sum = stuList.stream().
collect(Collectors.summingDouble(Student::getSalary));
System.out.println(sum);
//需求4:收集/获取学生工资最大值
Optional<Double> max = stuList.stream().map(Student::getSalary).
collect(Collectors.maxBy(Double::compareTo));
System.out.println(max.get());
//需求5:收集/获取学生工资最小值
Optional<Double> min = stuList.stream().map(Student::getSalary).
collect(Collectors.minBy(Double::compareTo));
System.out.println(min.get());
//需求6:收集/获取工资最多的学生信息
Optional<Student> maxStu = stuList.stream().
collect(Collectors.maxBy(
(stu1,stu2)-> (int)(stu1.getSalary()-stu2.getSalary())));
System.out.println(maxStu.get());
//需求7:收集/获取工资最少的学生信息
Optional<Student> minStu = stuList.stream().
collect(Collectors.minBy(
(stu1,stu2)-> (int)(stu1.getSalary()-stu2.getSalary())));
System.out.println(minStu.get());
}
@Test
public void test03() {//分组
//需求:按照学科分组
Map<Course, List<Student>> map = stuList.stream().collect(
Collectors.groupingBy(Student::getCourse));
System.out.println(map);
}
@Test
public void test04() {//多级分组
//需求:按照学科分组,在按照年龄分组
Map<Course, Map<String, List<Student>>> map = stuList.stream().
collect(Collectors.groupingBy(
Student::getCourse,Collectors.groupingBy((stu)->{
if(((Student)stu).getAge() < 28){
return "青年";
}else if(((Student)stu).getAge() < 40){
return "中年";
}else{
return "老年";
}
})));
System.out.println(map);
}
@Test
public void test05() {//分区
//需求:按照工资5000为标准分区
Map<Boolean, List<Student>> map = stuList.stream().collect(
Collectors.partitioningBy((stu) -> stu.getSalary()>5000));
System.out.println(map);
}
@Test
public void test06() {//获取元素中字段的各种信息
//需求:获取学生工资信息,再获取总值、平均值、最大值、最小值
DoubleSummaryStatistics collect = stuList.stream().collect(
Collectors.summarizingDouble(Student::getSalary));
System.out.println(collect.getSum());
System.out.println(collect.getAverage());
System.out.println(collect.getMax());
System.out.println(collect.getMin());
}
@Test
public void test07() {//拼接信息
//需求:拼接学生姓名
String str1 = stuList.stream().map(Student::getName).collect(
Collectors.joining());
System.out.println(str1);
String str2 = stuList.stream().map(Student::getName).collect(
Collectors.joining(","));
System.out.println(str2);
String str3 = stuList.stream().map(Student::getName).collect(
Collectors.joining(",","--","--"));
System.out.println(str3);
}
}
enum Course{//课程枚举
JAVA,HTML,PYTHON;
}
class Student implements Comparable<Student>{//学生类
private String name;
private int age;
private double salary;
private Course course;
...
}