- 速度更快
- 代码更少——Lambda
- 强大的Stream API
- 便于并行
- 最大化减少空指针异常——optional
速度更快指的是hashMap的结构增加了红黑树。
在之前的hashmap结构中,jdk采用的是数组+链表的方式进行。当发生碰撞时,会极大地降低查找的效率。所以在jdk1.8中,采用了数组+链表+红黑树的方式,前提是当发生碰撞的个数大于8时且总容量大于64时,才会将链表结构转换为红黑树,此时会造成添加元素的效率降低,但是查找和删除等的效率会大大提高:
一、Lambda表达式在数学中,函数就是有输入量、输出量的一套计算方案,也就是“拿什么东西做什么事情” 。相对而言,面向对过分强调“必须通过对象的形式来做事情”,而函数式思想则尽量忽略面向对象的复杂语法——强调做什么,而不是以什么形式做。
- 面向对象的思想:做一件事情,找一个能解决这个事情的对象,调用对象的方法,完成事情。
- 函数式编程思想:只要能获取到结果,谁去做的,怎么做的都不重要,重要的是结果,不重视过程。
Lambda 是一个匿名函数,我们可以把 Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
//原来的匿名内部类
@Test
public void test1(){
// 匿名内部类,即只存在实现的没有实例对象的类
Comparator com = new Comparator(){
@Override
public int compare(String o1, String o2) {
// 此句为核心的代码
return Integer.compare(o1.length(), o2.length());
}
};
TreeSet ts = new TreeSet<>(com);
}
//现在的 Lambda 表达式
@Test
public void test2(){
// 简化代码的书写,将核心代码抽取出来
// Lambda表达式作为参数传递
Comparator com = (x, y) -> Integer.compare(x.length(), y.length());
TreeSet ts = new TreeSet<>(com);
}
1.1 语法
Lambda 表达式在Java 语言中引入了一个新的语法元素和操作符。这个操作符为 “ ->” , 该操作符被称为 Lambda 操作符或箭头操作符。它将 Lambda 分为两个部分:
- 左侧: 指定了 Lambda 表达式需要的所有参数
- 右侧: 指定了 Lambda 体,即 Lambda 表达式要执行的功能
1.2 优缺点 1.1.1 优点上述 Lambda 表达式中的参数类型都是由编译器推断得出的。 Lambda 表达式中无需指定类型,程序依然可以编译,这是因为 javac编译器 根据程序的上下文,在后台推断出了参数的类型。 Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的“类型推断” 。
-
简洁。
-
非常容易并行计算。
-
可能代表未来的编程趋势。
-
结合 hashmap 的 computeIfAbsent 方法,递归运算非常快。java有针对递归的专门优化。
由于省略了太多东西,代码可读性有可能在一定程度上会降低。
1.3 实现拷贝小括号,写死右箭头,落地大括号
package com.zdp.java8;
import org.junit.Test;
import java.util.Comparator;
import java.util.function.Consumer;
public class LambdaStudy01 {
// Lambda体的作用是实现接口
// 前提是该接口中只含有一个抽象方法(函数式接口)
@Test
public void test1() {
// 同级别局部内部类
int num = 0;//jdk 1.7 前,必须是 final
// Runnable是一个无参无返回值的接口
Runnable r = new Runnable() {
@Override
public void run() {
// 匿名内部类引用同级别局部内部类
System.out.println("Hello World!" + num);
}
};
r.run();
System.out.println("-------------------------------");
// 语法格式一:无参数,无返回值
Runnable r1 = () -> System.out.println("Hello Lambda!");
r1.run();
}
// 语法格式二:有一个参数,并且无返回值
@Test
public void test2() {
// Lambda表达式 即去实现这个接口中的唯一一个方法
Consumer consumer = (x) -> System.out.println(x);
consumer.accept("zdp牛逼!!!");
}
// 语法格式三:若只有一个参数,小括号可以省略不写
@Test
public void test3() {
Consumer consumer = x -> System.out.println(x);
consumer.accept("zdp牛逼!!!");
}
// 语法格式四:有两个以上的参数,有返回值,并且 Lambda 体中有多条语句
@Test
public void test4() {
// 实现函数式接口
Comparator comparator = (a, b) -> {
// 多条语句
System.out.println("函数式接口");
return Integer.compare(a, b);
};
// 执行
int compare = comparator.compare(1, 2);
System.out.println(compare);
}
// 语法格式五:若 Lambda 体中只有一条语句, return 和 大括号 都可以省略不写
@Test
public void test5() {
// 实现函数式接口,实现时只有一条语句
// 语法格式六:Lambda 表达式的参数列表的数据类型可以省略不写
// 因为JVM编译器通过上下文推断出,数据类型,即“类型推断”
Comparator comparator = (a, b) -> Integer.compare(a, b);
// 执行
int compare = comparator.compare(1, 2);
System.out.println(compare);
}
}
二、 函数式接口
- 函数式接口有且仅有一个未被覆写的抽象方法。但是 Object 中方法不算,接口中的 default 方法和 static 方法也不算
@FunctionalInterface
public interface FunctionalInterfaceTest {
// 只含有一个抽象方法
void test();
// 默认实现方法除外
default void defaultTest(){
System.out.println("这是函数式接口的默认实现方法");
}
// 静态方法除外
static void staticTest(){
System.out.println("这是函数式接口的静态实现方法");
}
// Object的方法
String toString();
int hashCode();
boolean equals(Object object);
}
-
使用注解 @FunctionalInterface 修饰可以检查是否是函数式接口
2.1 自定义函数式接口default方法:
java8在接口中新增default方法,是为了在现有的类库中新增功能而不影响他们的实现类。试想一下,如果不增加默认实现的话,接口的所有实现类都要实现一遍这个方法,这会出现兼容性问题,如果定义了默认实现的话,那么实现类直接调用就可以了,并不需要实现这个方法。
// 增强的接口 public interface MyInterface01 { // 使用default关键字,增加接口方法的默认实现,该默认方法也可以被实现类重写 default void testInterFace(){ System.out.println("增强接口的默认方法"); } void test(); }注意:如果接口中的默认方法不能满足某个实现类需要,那么实现类可以覆盖默认方法。不用加default关键字,例如:
// 增强接口的实现类 public class InterfaceTest implements MyInterface01 { // // 重写接口的默认方法,也可以不重写 // @Override // public void testInterFace() { // // } @Override public void test() { System.out.println("增强接口的test"); } }
@FunctionalInterface
public interface FunctionalInterfaceDemo {
// 只含有一个抽象方法
Integer test(int x);
// 默认实现方法除外
default void defaultTest(){
System.out.println("这是函数式接口的默认实现方法");
}
// Object的方法
String toString();
int hashCode();
boolean equals(Object object);
}
2.1.1 普通实现
public class LambdaStudy02 {
@Test
public void test1(){
// 利用 Lambda体 实现函数式接口
FunctionalInterfaceDemo demo = x -> x*x;
// 执行接口中的方法
Integer result = demo.test(10);
System.out.println(result.intValue());
}
}
2.1.2 参数传递 Lambda 表达式
为了将 Lambda 表达式作为参数传递,接收 Lambda 表达式的参数类型必须是与该 Lambda 表达式兼容的函数式接口的类型。
@Test
public void test2(){
// 调用方法myFun,其中第二个参数是Lambda表达式
Integer result = myFun(100, x -> x + x);
System.out.println(result);
}
public Integer myFun(Integer num,FunctionalInterfaceDemo demo){
return demo.test(num);
}
2.2 核心函数式接口
函数式接口可以自定义,JAVA内部也提供了四个核心函数式接口。
public class LambdaStudy04 {
// 消费型
@Test
public void test1() {
consumerTest(10000.0, x -> {
x = x - 500;
System.out.println("剩余" + x + "元");
});
}
public void consumerTest(Double money, Consumer consumer) {
consumer.accept(money);
}
// 供给型
@Test
public void test2() {
// 参数是供给型接口的无参方法get()的实现,由于无参,所以使用()空括号
List result = supplierTest(10, () -> (int) (Math.random() * 100));
for (Integer num : result) {
System.out.println(num);
}
}
// 需求:产生指定个数的整数,并放入集合中
public List supplierTest(int num, Supplier supplier) {
List list = new ArrayList<>();
for (int i = 0; i < num; i++) {
// 调用供给型接口内部的方法
// supplier.get()此时的作用是:生成一个整数
list.add(supplier.get());
}
return list;
}
// 函数型
@Test
public void test3() {
// 实现体为去除空格处理
String s = functionTest("tttt lllllllllllllllllsaaaaaa", (str) -> str.trim());
System.out.println(s);
}
//需求:用于处理字符串
public String functionTest(String str, Function fun){
// apply方法指定要对字符串做什么处理,其具体实现在lambda中定义
return fun.apply(str);
}
//Predicate 断言型接口:
@Test
public void test4(){
List list = Arrays.asList("Hello", "atguigu", "Lambda", "www", "ok");
// 定义筛选的条件
List strList = predicateTest(list, (s) -> s.length() > 3);
for (String str : strList) {
System.out.println(str);
}
}
//需求:将满足条件的字符串,放入集合中
public List predicateTest(List list, Predicate pre){
List strList = new ArrayList<>();
for (String str : list) {
// 具体是什么条件,它是定义在lambda表达式中的
if(pre.test(str)){
strList.add(str);
}
}
return strList;
}
}
以Consumer 消费型接口为例:
三、方法引用与构造器引用 3.1 方法引用当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
实现抽象方法的参数列表必须与方法引用方法的参数列表保持一致!这就是在方法引用中参数可以省略的原因。
方法引用:使用操作符 “::” 将方法名和对象或类的名字分隔开来。 如下三种主要使用情况:
- 对象::实例方法
- 类::静态方法
- 类::实例方法
// 对象的引用 :: 实例方法名
@Test
public void test1() {
// 调用consumer核心函数式接口
//1 原始lambda实现
Consumer consumer = (x) -> System.out.println(x);
consumer.accept("张大炮666666666");
//2 方法引用实现
// 因为println()方法是一个实例方法,即已经被实现了
// 且void println(String x)是一个有参且无返回值的方法,所以它可以与consumer接口的accept方法相对应
Consumer consumer1 = System.out::println;
consumer1.accept("dddyyy6666666");
}
@Test
public void test2() {
Employee employee = new Employee();
// 调用supplier接口,T get()是一个无参有返回值的方法
//1 原始实现,方法体只有一句,省略return
Supplier supplier = () -> employee.getName();
String name1 = supplier.get();
System.out.println(name1);
//2 方法引用
// getAge()方法已在Employee中实现
Supplier supplier1 = employee::getAge;
Integer age = supplier1.get();
System.out.println(age);
}
3.2 构造器引用
构造器的参数列表需要与函数式接口中参数列表保持一致!
- 类名 :: new
// 构造器引用
// 注意:需要调用的构造器的参数列表要与函数式接口接口中抽象方法的参数列表保持一致
@Test
public void test7() {
// 调用Employee的一个参数的构造器
Function fun = Employee::new;
// 调用Employee的两个参数的构造器
BiFunction fun2 = Employee::new;
}
@Test
public void test6() {
Supplier sup = () -> new Employee();
System.out.println(sup.get());
System.out.println("------------------------------------");
// 因为supplier的get方法是无参的,所以自动匹配Employee类的无参构造器
Supplier sup2 = Employee::new;
System.out.println(sup2.get());
}
四、Stream API
Stream 流处理,首先要澄清的是 java8 中的 Stream 与 I/O 流 InputStream 和 OutputStream 是完全不同的概念。Stream 机制是针对集合迭代器的增强。流允许你用声明式的方式处理数据集合。
在对数据源(集合、数组等)进行传输的过程中,Stream机制会对其进行一系列的中间操作,但是最后的数据源是不会改变的,并且会产生一个新的Stream流。
集合讲的是数据,流讲的是计算.
一个Stream的操作就如上图,在一个管道内,分为三个步骤:
- 第一步是创建Stream,从集合、数组中获取一个流;
- 第二步是中间操作链,对数据进行处理;
- 第三步是终止操作,用来执行中间操作链,返回结果。
Java8 中的Collection 接口被扩展,提供了两个获取流的方法,这两个方法是default方法,也就是说所有实现Collection接口的接口都不需要实现就可以直接使用:
-
default Stream stream() : 返回一个顺序流。
-
default Stream parallelStream() : 返回一个并行流。
List4.1.2 由数组创建integerList = new ArrayList<>(); integerList.add(1); integerList.add(2); Stream stream = integerList.stream(); //顺序流 Stream stream1 = integerList.parallelStream(); //并行流
Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:
-
static Stream stream(T[] array): 返回一个流
-
重载形式,能够处理对应基本类型的数组:
public static IntStream stream(int[] array)
public static LongStream stream(long[] array)
public static DoubleStream stream(double[] array)
int[] intArray = {1,2,3};
IntStream stream = Arrays.stream(intArray);
4.1.3 由值创建
可以使用静态方法 Stream.of(),通过显示值创建一个流。它可以接收任意数量的参数。
- public static Stream of(T… values) : 返回一个流。
Stream4.1.4 由函数创建integerStream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8);
可以使用静态方法 Stream.iterate() 和 Stream.generate() 创建无限流。
-
迭代:public static Stream iterate(final T seed, final UnaryOperator f)
// iterate方法的参数是:seed和一个函数式接口 Stream
stream3 = Stream.iterate(0, x -> x + 2); //注意:使用无限流一定要配合limit(中间操作)截断,不然会无限制创建下去 stream3.limit(10).forEach(System.out::println); -
生成:public static Stream generate(Supplier s)
// generate方法的参数是Supplier核心函数式接口 Stream4.2 中间操作stream4 = Stream.generate(() -> Math.random() * 100); stream4.limit(20).forEach(System.out::println);
如果Stream只有中间操作是不会执行的,当执行终端操作的时候才会执行中间操作,这种方式称为延迟加载或惰性求值。多个中间操作组成一个中间操作链,除非流水线上触发终止操作,否则中间操作不会执行任何的处理,只有当执行终端操作的时候才会执行一遍中间操作链,此时会一次性全部处理。
4.2.1 筛选与切片处理 (1)filter()filter方法中是接收一个和Predicate函数对应Lambda表达式,返回一个布尔值,从流中过滤某些元素。
@Test
public void test1() {
// 使用集合方式创建流
Stream stream = emps.stream();
Stream stream1 = stream.filter(employee -> {
System.out.println("中间操作:过滤");
return employee.getAge() > 30;
});
// 终止操作:若无不执行终止操作的话,前面的中间操作也将不会执行
// 惰性求值
stream1.forEach(System.out::println);
}
(2)distinct()
去重,通过流所生成元素的 hashCode() 和 equals() 去除重复元素。
@Test
public void test3(){
emps.stream().distinct().forEach(System.out::println);
}
// employee重写hashCode() 和 equals() 方法,使用IDEA自动生成
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + id;
result = prime * result + ((name == null) ? 0 : name.hashCode());
long temp;
temp = Double.doubleToLongBits(salary);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Employee other = (Employee) obj;
if (age != other.age)
return false;
if (id != other.id)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (Double.doubleToLongBits(salary) != Double.doubleToLongBits(other.salary))
return false;
return true;
}
(3)limit()
截断流,使其元素不超过给定数量。如果元素的个数小于maxSize,那就获取所有元素。
@Test
public void test2() {
// 使用集合方式创建流
Stream stream = emps.stream();
Stream stream1 = stream.filter(employee -> employee.getSalary() > 6000)
.limit(3);
// 终止操作:若无不执行终止操作的话,前面的中间操作也将不会执行
stream1.forEach(System.out::println);
}
(4)skip()
跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补。
@Test
public void test4(){
emps.parallelStream()
.filter((e) -> e.getSalary() >= 5000)
.skip(2)
.forEach(System.out::println);
}
4.2.2 映射处理
(1)map()
接收一个Function函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。也就是转换操作,map还有三个应用于具体类型方法,分别是:mapToInt,mapToLong和mapToDouble。这三个方法也比较好理解,比如mapToInt就是把原始Stream转换成一个新的Stream,这个新生成的Stream中的元素都是int类型。这三个方法可以免除自动装箱/拆箱的额外消耗。
注:map内接收一个Function的函数:R apply(T t); 其内部需要有返回值,所以在map中的lambda表达式中都要设置return值。
@Test
public void test5() {
List strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
Stream stream = strList.stream()
.map(str -> str.toUpperCase()); // 对流中的每个数据都进行变大写操作【省略[return] str.toUpperCase()】
stream.forEach(System.out::println);
System.out.println("-----------lambda表达式/方法引用-------------------");
//1 利用map提取流中的名字
emps.stream()
// map内接收一个Function函数,需要有返回值
.map(employee -> employee.getName()) // 普通的lambda表达式只有一句,所以可以省略return
.forEach(System.out::println); // 方法引用
//2 利用map提取流中的名字
emps.stream()
// map内接收一个Function函数,需要有返回值
.map(Employee::getName) // 使用 类名::实例方法 进行方法引用
.forEach(System.out::println); // 方法引用
System.out.println("--------------流的嵌套-----------------");
strList.stream()
.map(StreamAPI_Mid::getCharacter) // 返回一个流对象
.forEach(sm -> { // 第一层遍历的还是流
sm.forEach(System.out::println); // 所以还需遍历一层,才可取到字符
}); // 返回效果:{{a,a,a},{b,b,b},{c,c,c}...}
System.out.println("----------flatMap-------------------");
strList.stream()
.flatMap(StreamAPI_Mid::getCharacter) // 返回一个流对象
.forEach(System.out::println); // flatMap只需一层遍历就可
// 返回效果:{a,a,a,b,b,b,c,c,c...}
}
// 遍历String类型的字符串,将其中的每一个字符都提取出来,并转换为流
public static Stream getCharacter(String string) {
List list = new ArrayList<>();
for (Character ch : string.toCharArray())
list.add(ch);
return list.stream();
}
(2)flatMap()
接收一个 Function 函数作为参数,将流中的每个值都转换成另一个流,然后把所有流连接成一个流。flatMap 也有三个应用于具体类型的方法,分别是:flatMapToInt、flatMapToLong、flatMapToDouble,其作用于 map 的三个衍生方法相同。
4.2.3 排序处理 (1)sorted()- 自然排序:即使用comparable
- 比较器排序:即使用comparator,进行自定义比较器排序
@Test
public void test6() {
List strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
// 自然排序,调用String类内部定义好的comparableTo方法
strList.stream()
.sorted()
.forEach(System.out::println);
System.out.println("-----------------------------------");
// 比较器排序
emps.stream()
.sorted((e1, e2) -> { // 需要两个参数
if (e1.getAge() == e2.getAge()) {
return e1.getName().compareTo(e2.getName());
} else {
return Integer.compare(e1.getAge(),e2.getAge());
}
}).forEach(System.out::println);
}
4.3 终止操作
终端操作执行中间操作链并返回结果。终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是void。
4.3.1 查找与匹配package com.zdp.java8.StreamAPI;
import com.zdp.java8.Employee;
import com.zdp.java8.Employee.Status;
import org.junit.Test;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
public class StreamAPI_Final {
List emps = Arrays.asList(
new Employee(102, "李四", 79, 6666.66, Status.BUSY),
new Employee(101, "张三", 18, 9999.99, Status.FREE),
new Employee(103, "王五", 28, 3333.33, Status.VOCATION),
new Employee(104, "赵六", 8, 7777.77, Status.BUSY),
new Employee(104, "赵六", 8, 7777.77, Status.FREE),
new Employee(104, "赵六", 8, 7777.77, Status.FREE),
new Employee(105, "田七", 38, 5555.55, Status.BUSY)
);
@Test
public void test1() {
// 检查是否匹配所有元素
boolean bl = emps.stream()
.allMatch((e) -> e.getStatus().equals(Status.BUSY));
System.out.println(bl);
// 检查是否至少匹配一个元素
boolean bl1 = emps.stream()
.anyMatch((e) -> e.getStatus().equals(Status.BUSY));
System.out.println(bl1);
// 检查是否没有匹配的元素
boolean bl2 = emps.stream()
.noneMatch((e) -> e.getStatus().equals(Status.BUSY));
System.out.println(bl2);
// 返回第一个元素
Optional first = emps.stream()
.sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())) // 工资最低的
.findFirst();
// first.orElse(new Employee());
Employee employee = first.get();
System.out.println(employee);
// 返回当前流中的任意元素(先过滤出所有空闲状态的人)
Optional any = emps.stream()
.filter(employee1 -> employee1.getStatus().equals(Status.FREE))
.findAny();
Employee employee1 = any.get();
System.out.println(employee1);
}
// 统计类终止操作
@Test
public void test2() {
// 返回流中元素的总个数
long count = emps.stream()
.count();
System.out.println(count);
// 返回最大值
Optional max = emps.stream()
.max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(max.get());
// 返回工资最小值
Optional min = emps.stream()
.map(Employee::getSalary) // 提取所有员工的工资
.min(Double::compare); // 使用方法引用
System.out.println(min.get());
}
}
4.3.2 归约
@Test
public void test3() {
List list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 因为有初始值 identity ,所以返回的是integer
Integer sum = list.stream()
.reduce(10, (x, y) -> x + y);// 对归约的 两个参数 的方法进行重写
System.out.println(sum);
System.out.println("----------------------------------------");
// 由于返回值可能为空,所以返回类型为Optional
Optional op = emps.stream()
.map(Employee::getSalary)
.reduce(Double::sum); // 对归约的 一个参数 的方法进行重写
System.out.println(op.get());
}
4.3.3 收集
Collector 接口中方法的实现决定了如何对流执行收集操作(如收集到 List、 Set、 Map)。但是 Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例, 具体方法与实例如下表:
注:收集的是新流中的数据
@Test
public void test4(){
List list = emps.stream()
.map(Employee::getName)
.collect(Collectors.toList());
list.forEach(System.out::println);
System.out.println("----------------------------------");
Set set = emps.stream()
.map(Employee::getName)
.collect(Collectors.toSet());
set.forEach(System.out::println);
System.out.println("----------------------------------");
HashSet hs = emps.stream()
.map(Employee::getName)
.collect(Collectors.toCollection(HashSet::new));
hs.forEach(System.out::println);
System.out.println("----------------------------------");
List
// 统计类收集
@Test
public void test5(){
// 总数
Long count = emps.stream()
.collect(Collectors.counting());
System.out.println(count);
// 平均值
Double aver = emps.stream()
.collect(Collectors.averagingDouble(Employee::getSalary));
System.out.println(aver);
// 总和
Double sum = emps.stream()
.collect(Collectors.summingDouble(Employee::getSalary));
System.out.println(new BigDecimal(sum));
// 拥有最大工资的员工信息
Optional max = emps.stream()
.collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
System.out.println(max.get());
// 只获取最小工资数
Optional minSalary = emps.stream()
.map(Employee::getSalary)
.collect(Collectors.minBy((s1, s2) -> Double.compare(s1, s2)));
System.out.println(minSalary);
// 综合统计工资的所有信息
DoubleSummaryStatistics collect = emps.stream()
.collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(collect);
System.out.println(collect.getMax());
System.out.println(collect.getAverage());
System.out.println(collect.getCount());
System.out.println(collect.getSum());
}
// 分组类收集
@Test
public void test6(){
//1 按工资分组
Map> collect = emps.stream()
.collect(Collectors.groupingBy(Employee::getSalary)); // 返回工资为Map的key
System.out.println(collect);
//2 按工作状态-年龄多级分组
Map>> collect1 = emps.stream()
.collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy(employee -> {
if (employee.getAge() < 35) {
return "青年";
} else return "中年";
})));
System.out.println(collect1);
//3 分区
Map> collect2 = emps.stream()
.collect(Collectors.partitioningBy(employee -> employee.getSalary() > 5000));
System.out.println(collect2);
}
4.4 流式计算SQL语句
public class StreamAPI_SQL {
public static void main(String[] args) {
Employee employee1 = new Employee("zdp1", 20,1001);
Employee employee2 = new Employee("zdp2", 30,1002);
Employee employee3 = new Employee("zdp3", 40,1003);
Employee employee4 = new Employee("zdp4", 50,1004);
Employee employee5 = new Employee("zdp5", 60,1005);
Employee employee6 = new Employee("zdp6", 70,1006);
Employee employee7 = new Employee("zdp7", 80,1007);
Employee employee8 = new Employee("zdp8", 90,1008);
// 创建Employee列表
List list = Arrays.asList(employee1, employee2, employee3, employee4, employee5, employee6, employee7, employee8);
// stream编程,筛选age大于50的且工资为偶数的员工信息
List employees = list.stream()
// 使用多个过滤中间操作
.filter((employee -> employee.getAge() >= 50))
.filter(employee -> employee.getSalary() % 2 == 0)
.collect(Collectors.toList()); // 收集
Iterator iterator = employees.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}



