Java8-Stream流详细教程
创始人
2024-03-13 12:17:25
0

前言

1、什么是Stream

前面我们讲了Lambda表达式与Optional类,下面我们将会使用这两个新特性,特别是Lambda
Stream 是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列

集合讲的是数据,Stream讲的是计算!

注意:

  • Stream自己不会存储元素
  • Stream不会改变源对象。相反,他们会返回一个特有结果的新Stream
  • Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行

2、为什么要使用StreamAPI

实际开发中,项目多数数据源都来自于Mysql,Oracle等。但现在数据源可以更多了,有MongDB,Radis等,而这些NoSQL的数据就需要Java层面去处理

Stream和Collection集合的区别:==Collectioin是一种静态的内存数据结构,而Stream是有关计算的。==前者主要面向内存,存储在内存中,后者主要是面向CPU,通过CPU实现计算。

2.1、举例

假设你有一个班所有学生的分数数据,你需要从中筛选出成绩不及格的,并且按照分数排序,打印按顺序学生的名称

JDK1.7写法

package com.tcc.test;import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;/*** @author 宇辰* @date 2022/8/31-8:53**/
public class Test{public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 56D),new Student("李四", 23, "101班", 44D),new Student("王五", 25, "102班", 44.5D));ArrayList list = new ArrayList<>();// 筛选出所有成绩不合格的学生for (Student s : students) {if (s.getScore() < 60){list.add(s);}}// 排序Comparator com = new Comparator() {@Overridepublic int compare(Object o1, Object o2) {Student stu1 = (Student) o1;Student stu2 = (Student) o2;// 按照分数排序return stu1.getScore().compareTo(stu2.getScore());}};list.sort(com);// 遍历得出所有名称/*李四王五张三*/for (Student s : list) {System.out.println(s.getName());}}}class Student{// 姓名private String name;// 年龄private Integer age;// 班级private String clazz;// 分数private Double score;public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public String getClazz() {return clazz;}public void setClazz(String clazz) {this.clazz = clazz;}public Double getScore() {return score;}public void setScore(Double score) {this.score = score;}public Student() {}public Student(String name, Integer age, String clazz, Double score) {this.name = name;this.age = age;this.clazz = clazz;this.score = score;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", clazz='" + clazz + '\'' +", score=" + score +'}';}
}

JDK1.8写法

public class Test{public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 56D),new Student("李四", 23, "101班", 44D),new Student("王五", 25, "102班", 44.5D));/*李四王五张三*/students.stream()// 中间操作,过滤小于60分的同学.filter(s -> s.getScore() < 60)// 中间操作,定制排序.sorted((s1,s2) -> Double.compare(s1.getScore(),s2.getScore()))// 中间操作,依次获取学生的姓名.map(s -> s.getName())// 终止操作,内部迭代打印获取到的每个学生的姓名.forEach(System.out::println);}}....省略Student类

2.2、执行步骤

在这里插入图片描述

3、StreamAPI说明

  • Java8中有两大最为重要的改变。第一个是Lambda表达式;另外一个是Stream API
  • ==Stream API(java.util.stream)==把真正的函数式编程风格引入到了Java中。这是目前为止对Java类库最好的补充,因为StreamAPI可以极大提供Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
  • Stream是Java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非重复杂的查找、过滤和映射数据等操作。使用==Stream API对集合数据进行操作,就类似于使用SQL执行的数据库查询。==也可以使用Stream API来并行执行操作。简言之,Stream API提供了一种高效且易于使用的处理数据的方式

4、Stream的操作三个步骤

1、创建Stream

一个数据源(如:集合、数组),获取一个流

2、中间操作

一个中间操作链,对数据源的数据进行处理

3、终止操作(终端操作)

一旦执行终止操作,就执行中间操作链,并产生结果:之后,不会再被使用

在这里插入图片描述

5、并行流与串行流

并行流:就是把一个内容分成多个数据块,并用不用的线程分别处理每个数据块的。相比较串行的流,并行的流可以很大程度上提高程序的执行效率。

Java 8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。

StreamAPI可以声明性地通过parallel()sequential()在并行流与顺序流之间进行切换

创建Stream流方式

以下所有演示代码都使用了Lambda表达式,如果不了解的话,可以阅读:Lamdba表达式快速学习与使用

1、通过集合

Java8 中的Collection接口被扩展,提供了两个获取流的方法:

  • default Stream stream():返回一个顺序流
  • default Stream parallelStream():返回一个并行流
public static void main(String[] args){ArrayList list = new ArrayList<>();// 创建一个串行流Stream stream = list.stream();// 创建一个并行流Stream parallelStream = list.parallelStream();
}

2、通过数组

Java8 中的Arrays 的静态方法stream()可以获取数组流:

  • seatic Stream stream(T[] array):返回一个流

重载形式,能够处理对应基本类型的数组:

  • public static IntStream stream(int[] array)
  • public static LongStream stream(logn[] array)
  • public static DoubleStream stream(double[] array)
public static void main(String[] args){String[] s = new String[16];// 串行流,没有提供创建并行流的方法Stream str1 = Arrays.stream(s);
}

3、通过Stream的of()

可以调用Stream类静态方法of(),通过显示值创建一个流。它可以接收任意数量的参数

  • public static Stream of(T… values):返回一个流
public static void main(String[] args){Stream stream = Stream.of(123, 222, 344);
}

4、由文件生产流

Java中用于处理文件等I/O操作的NIO API(非阻塞I/O)已更新,以便利用Stream API。java.nio.file.Files中的很多静态方法都会返回一个流。例如,一个很有用的方法:Files.lines,它会返回一个由指定文件中的各行构成的字符串流。

例子:差异一个文件中有多少各不相同的词

public static void main(String[] args){// 这样写会自动关闭流try (Stream lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset());){// 需要用flatMap方法扁平化返回值lines.flatMap(line -> Arrays.stream(line.split(" ")))// 中间操作,去重.distinct()// 终止操作,计数.count();}catch (IOException e){e.printStackTrace();}
}

5、创建无限流

可以使用静态方法Stream.iterate()和Stream.generate(),创建无限流。

  • 迭代
    • public static Stream iterate(final T seed,final UnaryOperator f)
  • 生成
    • public static Stream generate(Supplier s)

5.1、iterate

public static void main(String[] args){Stream iterate = Stream.iterate(0, t -> t + 2);// 中间操作,后面会讲到,用于截断流,取前三个Stream limit = iterate.limit(3);/*024*/// 终止操作 内部迭代limit.forEach(System.out::println);
}

5.2、generate

public static void main(String[] args){Stream generate = Stream.generate(Math::random);Stream limit = generate.limit(3);/*0.491710149749352570.70731851158800650.5253605859404055*/limit.forEach(System.out::println);
}

中间操作

多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,成为"惰性求值"

1、筛选与切片

  • filter:接收Lambda,从流汇总排除某些元素
  • distinct():筛选,通过流所生成元素的hashCode()和equals()去除重复元素
  • limit(long maxSize):截断流,使其元素不超过给定数量
  • skip(long n):跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n)互补

1.1、filter

该操作会接受一个谓词(一个返回boolean的函数)作为参数,并返回一个包括所有符合谓词的元素的流

比如说:你想获取所有成绩不及格的学生

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));students.stream()// 如果满足条件,就继续执行。不满足条件的剔除.filter(s -> s.getScore() < 60)/*Student{name='李四', age=21, clazz='101班', score=50.0}Student{name='王五', age=22, clazz='102班', score=44.5}Student{name='赵六', age=20, clazz='103班', score=44.5}*/.forEach(System.out::println);
}// ...Student类定义省略,前言第二部分举例里面有。。

在这里插入图片描述

1.2、distinct

去重,它会返回一个元素各异(根据流所生成元素的hashCode和equals方法实现)的流

比如说:你想知道同学的年龄都在哪个年龄段上有

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));students.stream()// 获取所有同学的年龄.map(Student::getAge)// 去重.distinct()/*202122*/.forEach(System.out::println);
}// ...Student类定义省略,前言第二部分举例里面有。。

在这里插入图片描述

1.3、limit

截短流,该方法会返回一个不超过给定长度的流。所需的长度作为参数传递给limit。

如果流式有序的,则最多会返回前n个元素。

比如说:你想获取成绩不及格的前两个人

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));students.stream()// 如果满足条件,就继续执行。不满足条件的剔除.filter(s -> s.getScore() < 60).limit(2)/*Student{name='李四', age=21, clazz='101班', score=50.0}Student{name='王五', age=22, clazz='102班', score=44.5}*/.forEach(System.out::println);
}// ...Student类定义省略,前言第二部分举例里面有。。

在这里插入图片描述

1.4、skip

流还支持skip(n)方法,返回一个扔掉了前n个元素的流。如果流中元素不足n个,则返回一个空流。

注意:limit(n)和skip(n)是互补的:

比如说:你想获取除了前两个后面的所有不及格的学生

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));students.stream()// 如果满足条件,就继续执行。不满足条件的剔除.filter(s -> s.getScore() < 60).skip(2)// Student{name='赵六', age=20, clazz='103班', score=44.5}.forEach(System.out::println);
}

在这里插入图片描述

2、映射

  • map(Function f):接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
  • flatMap(Function f):接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
  • mapToDouble(ToDoubleFunction f):接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的DoubleStream
  • mapToInt(ToIntFunction f):接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的IntStream
  • mapToLong(ToLongFunction f):接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的LongStream

2.1、map

流支持map方法,它会接受一个函数作为参数。这个函数会被应用到每个元素上,并将其映射成一个新的元素(使用映射一次,是因为它和转换类似,但其中的细微差别在于,它是"创建一个新的版本",而不是去"修改"原来的数据)

比如说:你想获取所有学生的姓名

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));students.stream().map(Student::getName)/*李四王五赵六田七*/.forEach(System.out::println);
}

在这里插入图片描述

比如说:你想再获取每个人名字有多长

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));students.stream().map(Student::getName).map(String::length)/*2222*/.forEach(System.out::println);
}

2.2、flatMap

flatMap方法让你把一个流中的每个值都换成另一个流,然后把所有流链接起来成为一个流

比如说:你想获取所有人名字不相同的字有哪些,我们先来使用map看看会有什么问题

2.1、使用map

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("张四", 21, "101班", 50D),new Student("李三", 20, "103班", 44.5D));// 你会发现它返回的不是你想要的Stream,而是里面包含了一层String[]Stream stream = students.stream().map(Student::getName).map(str -> str.split("")).distinct();// 这时候你需要在forEach里面再遍历strings数组,并没有达到去重的效果/*张三张四李三*/stream.forEach(strings -> {for (String s : strings) {System.out.println(s);}});
}

2.2、原因

在这里插入图片描述

2.3、使用flatMap

我们可以使用flatMap来简化上面代码

  1. 我们使用split方法获取到的是一个数组,我们上面学习到,可以使用Arrays.stream()方法,把数组转换为一个Stream流
  2. 转换为流后,我们的返回值就会为Stream>,这时候我们使用flatMap方法,把这个流中流,转换为单个流Stream

实践如下

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("张四", 21, "101班", 50D),new Student("李三", 20, "103班", 44.5D));students.stream().map(Student::getName).flatMap(str -> Arrays.stream(str.split(""))).distinct()/*张三四李*/.forEach(System.out::println);
}

2.4、执行流程如下

在这里插入图片描述

2.3、数值流

想IntSteam、DoubleStream、LongStream都属于数值流,我们会放到终止操作的第二章(归约)后面详细讲解

3、排序

  • sorted():产生一个新流,其中按自然顺序排序
  • sorted(Comparator com):产生一个新流,其中按比较器顺序排序

3.1、sorted()

根据元素类型内置的排序规则进行排序,默认升序排序

比如说:根据每个学生的分数进行排序

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));students.stream().map(Student::getScore)// 根据Double类内置的compareTo方法进行排序.sorted()/*44.544.550.088.091.0*/.forEach(System.out::println);
}

3.2、sorted(Comparator com)

根据自定义排序规则进行排序

比如说:根据每个学生分数进行排序,如果分数相同,则根据年龄排序

// 元数据
List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D)
);
students.stream().sorted((s1,s2) -> {int i = s1.getScore().compareTo(s2.getScore());if(i == 0){// 如果需要降序排序,前面加个 - 号即可return s1.getAge().compareTo(s2.getAge());}// 如果需要降序排序,前面加个 - 号即可return i;})/*Student{name='赵六', age=20, clazz='103班', score=44.5}Student{name='王五', age=22, clazz='102班', score=44.5}Student{name='李四', age=21, clazz='101班', score=50.0}Student{name='张三', age=20, clazz='100班', score=88.0}Student{name='田七', age=21, clazz='103班', score=91.0}*/.forEach(System.out::println);
}

终止操作

终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是void。

流进行了终止操作后,不能再次使用。可以再次生成一个新流进行使用

1、匹配与查找

  • allMatch(Predicate p):检查是否匹配所有元素
  • anyMatch(Predicate p):检查是否至少匹配一个元素
  • noneMatch(Predicate p):检查是否没有匹配所有元素
  • findFirst():返回第一个元素
  • findAny():返回当前流中的任意元素
  • count():返回流中元素总数
  • max(Comparator c):返回流中最大值
  • min(Comparator c):返回流中最小值
  • forEach(Consumer c):内部迭代(使用Collection接口需要用户去做迭代,成为外部迭代。相反,StreamAPI使用内部迭代———它帮你把迭代做了)

1.1、allMatch

检测流中所有元素是否都能匹配给定的谓词

比如说:你想查看是否所有学生成绩都及格了

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));boolean b = students.stream().allMatch(s -> s.getScore() >= 60);System.out.println(b); // false
}

1.2、anyMatch

检测流中是否有一个元素能匹配给定的谓词

比如说:你想查看是否有一个同学不及格

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));boolean b = students.stream().anyMatch(s -> s.getScore() < 60);System.out.println(b); // true
}

1.3、noneMatch

allMatch相反,检测流中所有元素是否都不能匹配给定的谓词

比如说:你想看看是否所有的学生都不是105班的

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));boolean b = students.stream().noneMatch(s -> "105".equals(s.getClazz()));System.out.println(b); // true
}

1.4、findFirst

有些流使用排序来指定集合中的顺序,对于这种元素,你可能想要找到第一个元素。

比如说:找到分数第一的学生

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 使用Optional可以防止找不到,空指针异常Optional student = students.parallelStream()// 使用Comparator的默认方法,reversed方法用于反转顺序,默认升序排序,需要降序排序,然后拿到第一名的学生.sorted(Comparator.comparingDouble(Student::getScore).reversed()).findFirst();System.out.println(student); // Optional[Student{name='田七', age=21, clazz='103班', score=91.0}]
}

1.5、findAny

findAny方法将返回当前流中任意元素。可以与其他流操作结合使用

比如说:你可能想抽取103班的任意一人

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 使用Optional可以防止找不到,空指针异常// 使用并行流可以直观看出(多运行几次,查看结果)Optional student = students.parallelStream().filter(s -> "103班".equals(s.getClazz())).findAny();System.out.println(student); // Optional[Student{name='赵六', age=20, clazz='103班', score=44.5}]}

1.6、count

比如说:你想知道有几个学生不及格

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));long count = students.stream().filter(s -> s.getScore() < 60).count();System.out.println(count); // 3
}

1.7、max

比如说:你想知道最高分是多少

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 从1-100的所有偶数个数Optional max = students.stream().map(Student::getScore).max(Double::compare);System.out.println(max); // Optional[91.0]
}

1.8、min

比如说:你想知道最低分是多少

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 从1-100的所有偶数个数Optional max = students.stream().map(Student::getScore).min(Double::compare);System.out.println(max); // Optional[44.5]
}

1.9、forEach

比如说:你想打印成绩不及格的学生姓名

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));students.stream().filter(s -> s.getScore() < 60)/*李四王五赵六*/.forEach(s -> System.out.println(s.getName()));
}

2、归约

  • reduce(T iden, BinaryOperator b):可以将流中元素反复结合起来,得到一个值。返回T
    • 参数一:变量的初始值
    • 参数二:将列表中所有元素结合在一起的操作
  • reduce(BinaryOperator b):可以将流中元素反复结合起来,的达到一个值。返回Optional

map和reduce的连接通常称为map-reduce模式,因Google用它来进行网络搜索而出名

2.1、reduce(T iden, BinaryOperator b)

总和:比如说:你想知到所有学生的总分是多少

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 使用Optional可以防止找不到,空指针异常Double reduce = students.stream()// 获取所有同学的分数.map(Student::getScore)//                .reduce(0D, (a, b) -> a + b);// 优化,Double有一个sum的静态方法,恰巧可以让我们使用方法引用.reduce(0D,Double::sum);System.out.println(reduce); // 318.0
}

在这里插入图片描述

最大值:比如说:你想知道最高分是多少

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 使用Optional可以防止找不到,空指针异常Optional reduce = students.stream().map(Student::getScore)//                .reduce((a, b) -> a>b?a:b);// 优化,Double有一个max的静态方法,恰巧可以让我们使用方法引用.reduce(Double::max);System.out.println(reduce); // Optional[91.0]
}

最小值:比如说:你想知道最低分是多少

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 使用Optional可以防止找不到,空指针异常Optional reduce = students.stream().map(Student::getScore)
//                .reduce((a, b) -> a>b?b:a);// 优化,Double有一个min的静态方法,恰巧可以让我们使用方法引用.reduce(Double::min);System.out.println(reduce); // Optional[44.5]}

2.2、reduce(BinaryOperator b)

为什么这个方法的返回值会是Optional类型的呢,考虑到流中没有任何元素的情况,reduce无法返回其和,因为它没有初始值

比如说:你想知到所有学生的总分是多少

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 使用Optional可以防止找不到,空指针异常Optional reduce = students.stream().map(Student::getScore)//                .reduce((a, b) -> a + b);// 优化,Double有一个sum的静态方法,恰巧可以让我们使用方法引用.reduce(Double::sum);System.out.println(reduce); // Optional[318.0]
}

3、map补充-数值流

  • 经过上面的归约计算求和,我们回想,如果直接有一个sum()的终端操作,不是更好,就不需要调用Double里面的方法了。

  • 但这是不可能的。问题在于map方法会生成一个Stream。虽然流中的元素是Double类型,但Stream接口没有定义sum方法。

  • 为什么没有呢?比方说你只有一个像Student那样的Stream,把每个学生加起来是没有任何意义的。

  • 但是不要担心,Stream API还提过了原始类型流特化,专门支持处理数值流的方法

原始类型流特化:

Java 8引入了三个原始类型流特化接口来解决这个问题,从而不面了暗含的装箱成本。每个接口都带来了进行常用数值归约的新方法,比如对数值流求和的sum,找到最大元素的max等…

  • 映射到数值流
    • mapToInt:将Stream类型转化为IntStream类型,执行专属的操作
    • mapToDouble:将Stream类型转化为DoubleStream类型,执行专属的操作
    • mapToLong:将Stream类型转化为LongStream类型,执行专属的操作
  • 转换回对象流:一旦有了数值流,你可能会想把它转换回非特化流
    • boxed:将原始流转换为一般流

3.1、mapToDouble

这里只讲解这一个,因为其他两个使用方法和这个一模一样,只是操作的数据类型不一致而已

比如说:在这里把上面使用reduce进行的求和、最大值、最小值再次优化一遍,并且再获取所有学生的平均分

  • 这里,你会看到OptionalDouble类,这是对于Optional原始类型的特化版本,有OptionalInt、OptionalDouble、OptionalLong三个。因为求和的例子很容易,因为它有一个默认值:0.但是,如果要计算IntStream中的最大元素,就得换个贩子了,因为0是错误的结果。
public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 分数总和DoubleStream doubleStream = students.stream().mapToDouble(Student::getScore);double sum = doubleStream.sum();System.out.println(sum); // 318.0// 最高分OptionalDouble max = students.stream().mapToDouble(Student::getScore).max();System.out.println(max); // OptionalDouble[91.0]// 最低分OptionalDouble min = students.stream().mapToDouble(Student::getScore).min();System.out.println(min); // OptionalDouble[44.5]// 平均分OptionalDouble average = students.stream().mapToDouble(Student::getScore).average();System.out.println(average); // OptionalDouble[63.6]
}

3.2、boxed

// 分数总和
DoubleStream doubleStream = students.stream().mapToDouble(Student::getScore);
Stream boxed = doubleStream.boxed();

3.3、数值范围

和数字打交道时,有一个常用的东西就是数值范围。比如,假设你想要生成1和100之间的所有数字。Java8引入了两个可以用于IntStream和LongStream的静态方法,帮助生成这种范围:range和rangeClosed。这两个方法都是第一个参数接收起始值,第二个参数接受结束值。但range是不包含结束值的,而rangeClosed则包含结束值。

比如说:你想知道1-100的所有偶数的个数

// 从1-100的所有偶数个数	如果是range则49个
long count = IntStream.rangeClosed(1, 100).filter(i -> i % 2 == 0).count();
System.out.println(count); // 50

4、收集

  • collect(Collector c):将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法

Collector接口中方法的实现决定了如何对流执行收集的操作(如收集到List、Set、Map)

另外,Collectors实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法如下:

  • List toList:把流中元素收集到List
  • Set toSet:把流中元素收集到Set
  • Collection toCollection:把流中元素收集到创建的集合
  • Long counting:计算流中的元素的个数
  • Integer/Long/Double summingInt/summingLong/summingDouble:对流中元素的整数属性求和
  • Double:averagingInt/averagingLong/averagingDouble:计算流中元素Integer属性的平均值
  • xxxSummaryStatistics summarizingInt/summarizingLong/summarizingDouble:收集流中Integer属性的统计值。如:平均值
  • String joining:连接流中每个字符串
  • Optional maxBy:根据比较器选择最大值
  • Optional minBy:根据比较器选择最小值
  • 归约产生的类型 reducing:从一个作为累加器的初始值开始,利用BinaryOperator与流中元素逐个结合,从而归约成单个值
  • 转换函数返回的类型 collectingAndThen:包裹另一个收集器,对其结果转换函数
  • Map groupingBy:根据某属性值对流分组,属性为K,结果为V
  • Map partitioningBy:根据true或false进行分区

4.1、toList

将流中所有元素都放进List里面,并返回这个List

比如说:你想获取所有的学生姓名,并放进List里面

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 1.7及之前写法ArrayList stuName = new ArrayList<>();for (Student student : students) {stuName.add(student.getName());}System.out.println(stuName); // 张三、李四、王五、赵六、田七// 1.8写法List stuNames = students.stream().map(Student::getName).collect(Collectors.toList());System.out.println(stuNames); // 张三、李四、王五、赵六、田七
}

4.2、toSet

将流中所有元素都放进Set里面,并返回这个Set集合。使用这个集合也可以进行去重操作

比如说:你想获取所有学生的年龄,并且去重(上面写过相同的案例,使用distinct),最后放到Set集合里

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 1.7及之前写法HashSet stuAge = new HashSet<>();for (Student student : students) {stuName.add(student.getAge());}System.out.println(stuAge); // 20、21、22// 1.8写法Set stuAges = students.stream().map(Student::getAge).collect(Collectors.toSet());System.out.println(stuAges); // 20、21、22
}

4.3、toCollection

参数为:Supplier collectionFactory类型

依旧是上面的需求,只不过需要放到LinkedList里面

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 1.7及之前写法HashSet stuAge = new HashSet<>();for (Student student : students) {stuAge.add(student.getAge());}LinkedList stuAge2 = new LinkedList<>();for (Integer integer : stuAge) {stuAge2.add(integer);}System.out.println(stuAge2); // 20、21、22// 1.8写法LinkedList stuAges = students.stream().map(Student::getAge).distinct().collect(Collectors.toCollection(LinkedList::new));System.out.println(stuAges); // 20、21、22
}

4.4、counting

对流中元素进行计数(与上面讲解的count终端操作功能一样)

比如你需要统计不及格的总共有多少个学生

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 1.7及之前写法int num = 0;for (Student student : students) {if (student.getScore() < 60){num ++;}}System.out.println(num); // 3// 1.8写法Long collects = students.stream().filter(s -> s.getScore() < 60).collect(Collectors.counting());System.out.println(collects); // 3
}

4.5、summingInt/summingLong/summingDouble

参数为:ToIntFunction类型,对流中所有Integer类型的数据求和

对数值类型操作,还可以使用上方讲解的特化流进行操作。

比如说:你想获取所有学生的年龄之和

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 1.7及之前写法Integer num = 0;for (Student student : students) {num += student.getAge();}System.out.println(num); // 104// 1.8写法Integer collects = students.stream().collect(Collectors.summingInt(Student::getAge));System.out.println(collects); // 104
}

4.6、averagingInt/averagingLong/averagingDouble

对Integer类型的数据求平均值

比如说:你想知道所有学生年龄和分数的平均值

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 1.7及之前写法Double num = 0D;int count = 0;int anum = 0;for (Student student : students) {count ++;num += student.getScore();anum += student.getAge();}// 分数double avg = num / count;System.out.println(avg); // 63.6// 年龄 int / int类型,返回值为int类型的话,会丢失精度Double age = Double.valueOf(anum / count);System.out.println(age); // 20.8// 1.8写法// 分数Double avgs = students.stream().collect(Collectors.averagingDouble(Student::getScore));System.out.println(avgs); // 104// 年龄Double ages = students.stream().collect(Collectors.averagingInt(Student::getAge));System.out.println(ages); // 20.8
}

4.7、summarizingInt/summarizingLong/summarizingDouble

统计Integer类型的所有统计值,比如:平均值、求和、最大值、最小值、计数,然后把所有结果放到``类型里面

比如说,你想知道这次考试,共有多少名学生参加了、最高分、最低分、所有学生的分均分,所有学生的总分为多少,这时候,按照上面的写法,你就需要为每个结果创建一个流,然后调用Collectors里面的不同的计算方法。Java为我们提供了一个总的计算方法,可以计算你所有需求的统计

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 1.7及之前写法,这里就不写了,太多了。。。// 1.8写法DoubleSummaryStatistics collect = students.stream().collect(Collectors.summarizingDouble(Student::getScore));// DoubleSummaryStatistics{count=5, sum=318.000000, min=44.500000, average=63.600000, max=91.000000}System.out.println(collect);// 最高分System.out.println(collect.getMax()); // 91.0// 最低分System.out.println(collect.getMin()); // 44.5// 平均值System.out.println(collect.getAverage()); // 63.6// 总和System.out.println(collect.getSum()); // 318.0// 总数System.out.println(collect.getCount()); // 5
}

4.8、joining

joining方法返回的收集器会把流中每个对象应用toString方法得到的所有字符串连接成一个字符串,内部使用了StringBuilder把生成的字符串逐个追加起来。如果Student类中有一个toString方法来返回学生的名称,则无需映射每个学生的名称就能得到相同的结果。

  • joining():直接拼接,没有拼接符
  • joining(CharSequence delimiter):中间使用指定拼接符进行拼接

比如说:你想把每个学生名称拼接起来

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 1.7及之前写法String name = "";for (Student student : students) {name += student.getName();}System.out.println(name); // 张三李四王五赵六田七// 1.8写法// joiningString collect = students.stream().map(Student::getName).collect(Collectors.joining());System.out.println(collect); // 张三李四王五赵六田七// joining(CharSequence delimiter)String collect2 = students.stream().map(Student::getName).collect(Collectors.joining(","));System.out.println(collect2); // 张三,李四,王五,赵六,田七
}

4.9、maxBy

根据自定义排序规则,进行定制排序,获取最大值

比如说:你想获取最高分学生的信息

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 1.7及之前写法Comparator comparator = new Comparator() {@Overridepublic int compare(Object o1, Object o2) {Student s1 = (Student)o1;Student s2 = (Student)o2;return -s1.getScore().compareTo(s2.getScore());}};students.sort(comparator);System.out.println(students.get(0)); // Student{name='田七', age=21, clazz='103班', score=91.0}// 1.8写法// joiningOptional collect = students.stream().collect(Collectors.maxBy(Comparator.comparingDouble(Student::getScore)));System.out.println(collect); // Optional[Student{name='田七', age=21, clazz='103班', score=91.0}]
}

4.10、minBy

根据自定义排序规则,进行定制排序,获取最小值

比如说:你想获取最低分学生的信息

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 1.7及之前写法Comparator comparator = new Comparator() {@Overridepublic int compare(Object o1, Object o2) {Student s1 = (Student)o1;Student s2 = (Student)o2;return s1.getScore().compareTo(s2.getScore());}};students.sort(comparator);System.out.println(students.get(0)); // Student{name='王五', age=22, clazz='102班', score=44.5}// 1.8写法// joiningOptional collect = students.stream().collect(Collectors.minBy(Comparator.comparingDouble(Student::getScore)));System.out.println(collect); // Optional[Student{name='王五', age=22, clazz='102班', score=44.5}]
}

4.11、reducing

有三个重载方法

  • Collector reducing(T identity, BinaryOperator op):
  • Collector reducing(BinaryOperator op):
  • Collector reducing(U identity, Function mapper,BinaryOperator op)
    • 第一个参数是归约操作的起始值,也是流中没有元素时的返回值,所以很显然对于数值和而言,0是一个合适的值
    • 第二个参数相当于是map方法,将Student类中的用于计算的数值提取出来
    • 第三个参数是一个BinaryOperator,将两个项目累计成一个同类型的值。这里就是对Double类型进行求和

跟上面讲的reduce归约基本一样,只有参数有差异

比如说:你想获取所有学生的总分

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 1.7及之前写法// 1.8写法Double collect1 = students.stream().collect(Collectors.reducing(0D, Student::getScore, Double::sum));System.out.println(collect1); // 318.0Optional collect2 = students.stream().map(Student::getScore).collect(Collectors.reducing(Double::sum));System.out.println(collect2); // Optional[318.0]Double aDouble = new Double(82D);Double collect3 = students.stream().map(Student::getScore).collect(Collectors.reducing(aDouble, Double::sum));System.out.println(collect3); // 400.0
}

4.12、collectingAndThen

包裹另一个收集器,对其结果转换函数

比如说,你想使用上面第二种写法,但是又不想自己单独拆掉Optional的包装,可以使用如下方法

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 1.7及之前写法// 1.8写法Double collect = students.stream().map(Student::getScore).collect(Collectors.collectingAndThen(Collectors.reducing(Double::sum), Optional::get));System.out.println(collect); // 318.0
}

4.13、groupingBy

分组(重要)

根据某属性值对流分组,属性为K结果为V

  • groupingBy(Function classifier):根据指定数据进行分组
  • groupingBy(Function classifier, Collector downstream):分好组后,可以对分完组的List进行其他操作,可以通过该方法,对分好组的集合进行统计、取最大值、最小值等操作
  • groupingBy(Function classifier, Supplier mapFactory, Collector downstream):不晓得,很少用。。。
  • 可以进行单级分组,
  • 也可以进行多级分组

单级分组

groupingBy(Function classifier)

比如说,你想按照班级对各个班的学生进行分组

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 1.7及之前写法HashMap> map = new HashMap<>();ArrayList list = new ArrayList<>();for (Student student : students) {String clazz = student.getClazz();List l = map.get(clazz);// 如果分组不存在,则创建一个新分组if (l == null){ArrayList lis = new ArrayList<>();lis.add(student);map.put(clazz,lis);continue;}l.add(student);}/*key:100班value:[Student{name='张三', age=20, clazz='100班', score=88.0}]key:101班value:[Student{name='李四', age=21, clazz='101班', score=50.0}]key:102班value:[Student{name='王五', age=22, clazz='102班', score=44.5}]key:103班value:[Student{name='赵六', age=20, clazz='103班', score=44.5}, Student{name='田七', age=21, clazz='103班', score=91.0}]*/Set>> entries = map.entrySet();for (Map.Entry> entry : entries) {System.out.println("key:"+ entry.getKey());System.out.println("value:"+ entry.getValue());}System.out.println("******************************************");// 1.8写法Map> collect = students.stream().collect(Collectors.groupingBy(Student::getClazz));Set>> entries2 = collect.entrySet();/*key:100班value:[Student{name='张三', age=20, clazz='100班', score=88.0}]key:101班value:[Student{name='李四', age=21, clazz='101班', score=50.0}]key:102班value:[Student{name='王五', age=22, clazz='102班', score=44.5}]key:103班value:[Student{name='赵六', age=20, clazz='103班', score=44.5}, Student{name='田七', age=21, clazz='103班', score=91.0}]*/for (Map.Entry> entry : entries2) {System.out.println("key:"+ entry.getKey());System.out.println("value:"+ entry.getValue());}
}

groupingBy(Function classifier, Collector downstream)

比如说:你想统计每个班的人数

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "101班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D));// 1.7及之前写法HashMap> map = new HashMap<>();ArrayList list = new ArrayList<>();for (Student student : students) {String clazz = student.getClazz();List l = map.get(clazz);// 如果分组不存在,则创建一个新分组if (l == null){ArrayList lis = new ArrayList<>();lis.add(student);map.put(clazz,lis);continue;}l.add(student);}Set>> entries = map.entrySet();HashMap map2 = new HashMap<>();for (Map.Entry> entry : entries) {map2.put(entry.getKey(),entry.getValue().size());}System.out.println(map2); // {100班=1, 101班=1, 102班=1, 103班=2}System.out.println("******************************************");// 1.8写法Map collect = students.stream().collect(Collectors.groupingBy(Student::getClazz, Collectors.counting()));System.out.println(collect); // {100班=1, 101班=1, 102班=1, 103班=2}
}

多级分组

groupingBy(Function classifier, Collector downstream)

groupBy方法里面可以嵌套groupBy,进行多级分组

比如说:你想对班级分完组后,对合格和不合格的学生分下组···

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D),new Student("李四", 21, "100班", 50D),new Student("王五", 22, "102班", 44.5D),new Student("赵六", 20, "103班", 44.5D),new Student("田七", 21, "103班", 91D),new Student("小八", 21, "103班", 61D));// 1.7及之前写法(如果不想了解的可以直接看下面1.8写法)HashMap>> clazzMap = new HashMap<>();for (Student student : students) {Map> clazzMap2 = clazzMap.get(student.getClazz());// 判断外部map是否分组if(clazzMap2 == null){HashMap> cMap = new HashMap<>();clazzMap.put(student.getClazz(),cMap);}// 记录当前学生成绩状态String isHeGe = "";if(student.getScore() > 60){isHeGe = "及格";}else {isHeGe = "不及格";}// 重新获取分组clazzMap2 = clazzMap.get(student.getClazz());// 判断内部map是否分组List isPassList = clazzMap2.get(isHeGe);if (isPassList == null){ArrayList sList = new ArrayList<>();sList.add(student);clazzMap2.put(isHeGe,sList);continue;}isPassList.add(student);}/*班级:100班不及格:[Student{name='李四', age=21, clazz='100班', score=50.0}]及格:[Student{name='张三', age=20, clazz='100班', score=88.0}]--------------------班级:102班不及格:[Student{name='王五', age=22, clazz='102班', score=44.5}]--------------------班级:103班不及格:[Student{name='赵六', age=20, clazz='103班', score=44.5}]及格:[Student{name='田七', age=21, clazz='103班', score=91.0}, Student{name='小八', age=21, clazz='103班', score=61.0}]*/Set>>> entrySet = clazzMap.entrySet();entrySet.forEach(m -> {System.out.println("--------------------");System.out.println("班级:" + m.getKey());Map> value = m.getValue();Set>> entries = value.entrySet();entries.forEach(map -> {System.out.println(map.getKey() + ":" + map.getValue());});});System.out.println("******************************************");// 1.8写法Map>> collect = students.stream().collect(Collectors.groupingBy(Student::getClazz, Collectors.groupingBy(s -> {if (s.getScore() >= 60) {return "及格";} else {return "不及格";}})));Set>>> entries2 = collect.entrySet();/*班级:100班不及格:[Student{name='李四', age=21, clazz='100班', score=50.0}]及格:[Student{name='张三', age=20, clazz='100班', score=88.0}]--------------------班级:102班不及格:[Student{name='王五', age=22, clazz='102班', score=44.5}]--------------------班级:103班不及格:[Student{name='赵六', age=20, clazz='103班', score=44.5}]及格:[Student{name='田七', age=21, clazz='103班', score=91.0}, Student{name='小八', age=21, clazz='103班', score=61.0}]*/entries2.forEach(m -> {System.out.println("--------------------");System.out.println("班级:" + m.getKey());Map> value = m.getValue();Set>> entries = value.entrySet();entries.forEach(map -> {System.out.println(map.getKey() + ":" + map.getValue());});});
}

4.14、partitioningBy

分区

分区的好处在于保留了分区函数返回truefalse的两套流元素列表,分区的依据一定要是布尔类型

  • partitioningBy(Predicate predicate):按照指定的条件分组
  • partitioningBy(Predicate predicate, Collector downstream):可以对分好的组再次进行操作,可以通过该方法,对分好组的集合进行统计、取最大值、最小值等操作

这里我们在Student类里面新加一个属性,并重新生成有参构造和getter/setter方法

// 是否在校
private Boolean isAtSchool;

单级分区

partitioningBy(Predicate predicate)

比如说:你想根据学生是否在校分成两个区域,一个在校的所有学生,一个不在校的学生

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D,true),new Student("李四", 21, "100班", 50D,true),new Student("王五", 22, "102班", 44.5D,false),new Student("赵六", 20, "103班", 44.5D,true),new Student("田七", 21, "103班", 91D,false),new Student("小八", 21, "103班", 61D,false));// 1.7及之前写法HashMap> map = new HashMap<>();for (Student student : students) {Boolean atSchool = false;// 先获取学生是否在校if (student.getAtSchool()){atSchool = true;}List list = map.get(atSchool);// 如果没有创建过分组if(list == null){ArrayList l = new ArrayList<>();l.add(student);map.put(atSchool,l);continue;}// 创建过分组list.add(student);}Set>> entries1 = map.entrySet();/*key:falsevalue:[Student{name='王五', age=22, clazz='102班', score=44.5, isAtSchool=false}, Student{name='田七', age=21, clazz='103班', score=91.0, isAtSchool=false}, Student{name='小八', age=21, clazz='103班', score=61.0, isAtSchool=false}]key:truevalue:[Student{name='张三', age=20, clazz='100班', score=88.0, isAtSchool=true}, Student{name='李四', age=21, clazz='100班', score=50.0, isAtSchool=true}, Student{name='赵六', age=20, clazz='103班', score=44.5, isAtSchool=true}]*/entries1.forEach(entry -> {System.out.println("key:" + entry.getKey());System.out.println("value:" + entry.getValue());});System.out.println("******************************************");// 1.8写法Map> collect = students.stream().collect(Collectors.partitioningBy(Student::getAtSchool));Set>> entries = collect.entrySet();/*key:falsevalue:[Student{name='王五', age=22, clazz='102班', score=44.5, isAtSchool=false},Student{name='田七', age=21, clazz='103班', score=91.0, isAtSchool=false},Student{name='小八', age=21, clazz='103班', score=61.0, isAtSchool=false}]key:truevalue:[Student{name='张三', age=20, clazz='100班', score=88.0, isAtSchool=true},Student{name='李四', age=21, clazz='100班', score=50.0, isAtSchool=true},Student{name='赵六', age=20, clazz='103班', score=44.5, isAtSchool=true}]*/entries.forEach(entry -> {System.out.println("key:" + entry.getKey());System.out.println("value:" + entry.getValue());});
}

partitioningBy(Predicate predicate, Collector downstream)

比如说:你想知道在校生和不在校生的人数

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D,true),new Student("李四", 21, "100班", 50D,true),new Student("王五", 22, "102班", 44.5D,false),new Student("赵六", 20, "103班", 44.5D,true),new Student("田七", 21, "103班", 91D,false),new Student("小八", 21, "103班", 61D,false));// 1.7及之前写法HashMap> map = new HashMap<>();for (Student student : students) {// 先获取学生是否在校Boolean atSchool = student.getAtSchool();// 尝试获取分组List list = map.get(atSchool);// 如果没有创建过分组if(list == null){ArrayList l = new ArrayList<>();l.add(student);map.put(atSchool,l);continue;}// 创建过分组list.add(student);}Set>> entries1 = map.entrySet();entries1.forEach(entry -> {/*false=3true=3*/System.out.println(entry.getKey() + "=" + entry.getValue().size());});System.out.println("******************************************");// 1.8写法Map collect = students.stream().collect(Collectors.partitioningBy(Student::getAtSchool, Collectors.counting()));System.out.println(collect); // {false=3, true=3}
}

多级分区

partitioningBy(Predicate predicate, Collector downstream)

比如说:你想对是否在校的学生分完区后,再根据成绩是否合格进行分区显示

public static void main(String[] args){// 元数据List students = Arrays.asList(new Student("张三", 20, "100班", 88D,true),new Student("李四", 21, "100班", 50D,true),new Student("王五", 22, "102班", 44.5D,false),new Student("赵六", 20, "103班", 44.5D,true),new Student("田七", 21, "103班", 91D,false),new Student("小八", 21, "103班", 61D,false));// 1.7及之前写法HashMap>> map = new HashMap<>();for (Student student : students) {Boolean atSchool = student.getAtSchool();Map> atSchoolGroupMap = map.get(atSchool);// 如果没有根据是否在校分过组if (atSchoolGroupMap == null){// 创建一个空的内map分组(根据是否合格分组)HashMap> map1 = new HashMap<>();map.put(atSchool,map1);}// 获取当前学生是否合格Boolean isHeGe = false;if (student.getScore() > 60){isHeGe = true;}// 重新获取mapMap> atSchoolGroupMap2 = map.get(atSchool);List stu = atSchoolGroupMap2.get(isHeGe);// 如果为null,说明没有创建学生是否合格的分组// 就创建一个集合,把当前新建的分组添加到内map中if (stu == null) {ArrayList list = new ArrayList<>();list.add(student);atSchoolGroupMap2.put(isHeGe,list);// 当前数据处理完毕,处理下一条continue;}// 如果有集合了,就直接添加进去即可stu.add(student);}Set>>> entries1 = map.entrySet();/*key:false成绩是否合格:false结果:[Student{name='王五', age=22, clazz='102班', score=44.5, isAtSchool=false}]成绩是否合格:true结果:[Student{name='田七', age=21, clazz='103班', score=91.0, isAtSchool=false}, Student{name='小八', age=21, clazz='103班', score=61.0, isAtSchool=false}]----------------------------------------key:true成绩是否合格:false结果:[Student{name='李四', age=21, clazz='100班', score=50.0, isAtSchool=true}, Student{name='赵六', age=20, clazz='103班', score=44.5, isAtSchool=true}]成绩是否合格:true结果:[Student{name='张三', age=20, clazz='100班', score=88.0, isAtSchool=true}]*/entries1.forEach(entrie -> {System.out.println("----------------------------------------");System.out.println("key:" + entrie.getKey());Map> value = entrie.getValue();Set>> val = value.entrySet();val.forEach( v -> {System.out.println("成绩是否合格:" + v.getKey());System.out.println("结果:" + v.getValue());});});System.out.println("***************版本分界线**************************");// 1.8写法Map>> collect = students.stream().collect(Collectors.partitioningBy(Student::getAtSchool, Collectors.partitioningBy(s -> {if (s.getScore() > 60) {return true;}return false;})));Set>>> entries = collect.entrySet();/*key2:false成绩是否合格:false结果:[Student{name='王五', age=22, clazz='102班', score=44.5, isAtSchool=false}]成绩是否合格:true结果:[Student{name='田七', age=21, clazz='103班', score=91.0, isAtSchool=false},Student{name='小八', age=21, clazz='103班', score=61.0, isAtSchool=false}]----------------------------------------key:true成绩是否合格:false结果:[Student{name='李四', age=21, clazz='100班', score=50.0, isAtSchool=true},Student{name='赵六', age=20, clazz='103班', score=44.5, isAtSchool=true}]成绩是否合格:true结果:[Student{name='张三', age=20, clazz='100班', score=88.0, isAtSchool=true}]*/entries.forEach(entrie -> {System.out.println("----------------------------------------");System.out.println("key:" + entrie.getKey());Map> value = entrie.getValue();Set>> val = value.entrySet();val.forEach( v -> {System.out.println("成绩是否合格" + v.getKey());System.out.println("结果" + v.getValue());});});
}

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...
有效的括号 一、题目 给定一个只包括 '(',')','{','}'...
【Ctfer训练计划】——(三... 作者名:Demo不是emo  主页面链接:主页传送门 创作初心ÿ...