目录
16.1:Lambda表达式
16.2:函数式(Function)接口
16.3:方法引用和构造器引用
16.4:强大的Stream API
①Stream实例化
②Stream的中间操作
③终止操作
16.5:Option类
package com.jiayifeng.java;
import org.junit.Test;
import java.util.Comparator;
import java.util.function.Consumer;
/**
* author 爱编程的小贾
* create 2023-10-19 12:29
*
* 一:Lambda表达式的使用
* 1.举例:(o1,o2) -> Integer.compare(o1,o2);
* 2.格式:
* "->":Lambda操作符或箭头操作符
* "->"左边:Lambda形参列表(也就是接口中的抽象方法的形参列表)
* "->"左边:Lambda体(也就是重写的抽象方法的方法体)
*
* 3.Lambda表达式的使用:(六种情况)
*
* 4.Lambda表达式的本质:作为函数式接口的实例
* 如果一个接口中只声明了一个抽象方法,则此接口就称之为函数式接口。
* 我们可以在一个接口上使用@FunctionalInterface注解,这样可以检验它
* 是否是一个函数式接口
*
*
* 5.总结
* "->"左边:Lambda形参列表的参数类型可以省略(类型推断);
* 如果Lambda形参列表只有一个参数,其一对()也可以省略
* "->"右边:Lambda体应使用一对{}包裹;
* 如果Lambda体只有一条执行语句(也可能是return语句),可以省略这一对{}和return关键字
*
*/
public class Lambda {
@Test
public void test1(){
Runnable r1 = new Runnable(){
@Override
public void run() {
System.out.println("我爱北京天安门!");
}
};
r1.run();
System.out.println("************");
// Lambda表达式的写法
Runnable r2 = () -> System.out.println("我爱北京故宫!");
r2.run();
System.out.println("************");
// 方法引用
Comparator com3 = Integer::compare;
int compare = com3.compare(32, 21);
System.out.println(compare);
}
// 语法格式一:无参、无返回值
@Test
public void test2(){
Runnable r1 = new Runnable(){
@Override
public void run() {
System.out.println("我爱北京天安门!");
}
};
r1.run();
System.out.println("************");
// Lambda表达式的写法
Runnable r2 = () -> {
System.out.println("我爱北京故宫!");
};
r2.run();
}
// 语法格式二:Lambda需要一个参数,但是没有返回值
@Test
public void test3(){
Consumer con = new Consumer() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
con.accept("谎言和誓言的区别是什么?");
System.out.println("**************");
Consumer con1 = (String s) -> {
System.out.println(s);
};
con1.accept("一个是听的人当真了,一个是说的人当真了");
}
// 语法格式三:数据类型可以省略,因为可由编译推断得出,称为“类型推断”
@Test
public void test4(){
Consumer con1 = (String s) -> {
System.out.println(s);
};
con1.accept("一个是听的人当真了,一个是说的人当真了");
System.out.println("******************");
Consumer con2 = (s) -> {
System.out.println(s);
};
con1.accept("一个是听的人当真了,一个是说的人当真了!");
}
// 语法格式四:Lambda若只需要一个参数时,参数的小括号可以省略
@Test
public void test5(){
Consumer con1 = s -> {
System.out.println(s);
};
con1.accept("一个是听的人当真了,一个是说的人当真了");
}
// 语法格式五:Lambda需要两个或两个以上的参数,多条执行语句,并且可以有返回值
@Test
public void test6() {
Comparator com1 = new Comparator() {
@Override
public int compare(Integer o1, Integer o2) {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
}
};
System.out.println(com1.compare(12,21));
System.out.println("**************");
Comparator com2 = (o1, o2) -> {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
};
System.out.println(com2.compare(12,6));
}
// 语法格式六:当Lambda体只有一条语句时,return与大括号若有,都可以省略
@Test
public void test7(){
Comparator com1 = (o1, o2) -> {
return o1.compareTo(o2);
};
System.out.println(com1.compare(12,6));
System.out.println("***********");
Comparator com2 = (o1, o2) -> o1.compareTo(o2);
System.out.println(com1.compare(12,9));
}
}
函数式接口 | 参数类型 | 返回类型 | 用途 |
Consumer |
T | void | 对类型为T的对象应用操作,包含方法:void accept(T t) |
Supplier |
无 | T | 返回类型为T的对象,包含方法:T get() |
Function |
T | R | 对类型为T的对象应用操作,并返回结果,结果是R类型的对象。包含方法:R apply(T t) |
Predicate |
T | boolean | 确定类型为T的对象是否满足某约束,并返回boolean值。包含方法:boolean test(T t) |
package com.jiayifeng.java;
import org.junit.Test;
import java.util.function.Consumer;
/**
* author 爱编程的小贾
* create 2023-10-19 16:51
*
* 一:Java内置的四大核心函数式接口
*/
public class LambdaTest1 {
@Test
public void test1(){
happyTime(500, new Consumer() {
@Override
public void accept(Double aDouble) {
System.out.println("学习太累了,去超市买瓶水,价格为:" + aDouble);
}
});
System.out.println("**************");
happyTime(400,money -> System.out.println("学习太累了,去超市喝了瓶水,价格为:" + money));
}
public void happyTime(double money, Consumer con){
con.accept(money);
}
}
package com.jiayifeng.java;
import org.junit.Test;
import java.io.PrintStream;
import java.util.function.Consumer;
/**
* author 爱编程的小贾
* create 2023-10-19 17:10
*
* 一:方法引用和构造器引用
* 1.使用情景:当要传递给Lambda体的操作已经有实现的方法了,可以使用方法引用
* 2.方法引用,本质上就是Lambda表达式,而Lambda表达式作为函数式接口的实例。
* 所以方法引用,也是函数式接口的实例
* 3.使用格式:类(或对象) :: 方法名
* 4.具体分为如下的三种情况:
* 对象 :: 非静态方法
* 类 :: 静态方法
* 类 :: 非静态方法
* 5.方法引用使用的要求:要求接口中的抽象方法的形参列表和返回值类型与
* 方法引用的方法的形参列表和返回值类型相同(针对于前两种情况)
*/
public class MethodRefTest {
@Test
public void test1(){
// 情况一:对象 :: 实例方法
// Consumer中的void accept(T t)
// PrintStream中的void Println(T t)
Consumer con1 = str -> System.out.println(str);
con1.accept("北京");
System.out.println("**********");
PrintStream ps = System.out;
Consumer con2 = ps :: println;
con2.accept("beijing");
}
}
构造器引用:
和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致
数组引用:
可以将数组看做是一个特殊的类,写法与构造器引用一致
使用Stream API对数据集合进行操作,就类似于使用SQL执行的数据库查询
简言之:Stream API提供了一种高效且易于使用的处理数据的方式
定义:Stream是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列
“集合讲的是数据,Stream讲的是计算”
注意:
①Stream自己不会存储数据
②Stream不会改变源对象。相反,他们会返回一个持有结果的新Stream
③Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行
执行流程:
package com.jiayifeng.java;
/**
* author 爱编程的小贾
* create 2023-10-19 19:34
*/
public class Employee {
private int id;
private String name;
private int age;
private double salary;
public Employee(int id) {
this.id = id;
}
public Employee(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public Employee(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Employee)) return false;
Employee employee = (Employee) o;
if (getId() != employee.getId()) return false;
if (getAge() != employee.getAge()) return false;
if (Double.compare(employee.getSalary(), getSalary()) != 0) return false;
return getName() != null ? getName().equals(employee.getName()) : employee.getName() == null;
}
@Override
public int hashCode() {
int result;
long temp;
result = getId();
result = 31 * result + (getName() != null ? getName().hashCode() : 0);
result = 31 * result + getAge();
temp = Double.doubleToLongBits(getSalary());
result = 31 * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", salary=" + salary +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public Employee() {
}
public Employee(int id, String name, int age, double salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
}
package com.jiayifeng.java;
import java.util.ArrayList;
import java.util.List;
/**
* author 爱编程的小贾
* create 2023-10-19 19:35
*/
public class EmployeeData {
public static List getEmp() {
List list = new ArrayList<>();
list.add(new Employee(1001, "马化腾", 34, 6000.38));
list.add(new Employee(1002, "马云", 12, 9876.12));
list.add(new Employee(1003, "刘强东", 33, 3000.82));
list.add(new Employee(1004, "雷军", 26, 7657.37));
list.add(new Employee(1005, "李彦宏", 65, 5555.21));
list.add(new Employee(1006, "比尔盖茨", 42, 9500.43));
list.add(new Employee(1006, "任正非", 26, 4333.32));
list.add(new Employee(1006, "扎克伯格", 35, 2500.32));
return list;
}
}
package com.jiayifeng.java;
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
* author 爱编程的小贾
* create 2023-10-19 19:26
*
* 一:Stream API
* 1.Stream关注的是对数据的运算,与CPU打交道
* 集合关注的是数据的存储,与内存打交道
*
* 2.Stream执行流程
* ①Stream实例化
* ②一系列的中间操作(过滤、映射等)
* ③终止操作
*
* 3.说明
* ①一个中间操作链,对数据源的数据进行处理
* ②一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被调用
*
*/
public class StreamAPI {
/**
* 创建Stream方式1:通过集合List
*/
@Test
public void test1() {
List emp = EmployeeData.getEmp();
//通过集合来创建
//顺序流
Stream stream = emp.stream();
//并行流
Stream employeeStream = emp.parallelStream();
}
/**
* 创建Stream方式2:通过数组
*/
@Test
public void test2() {
int arr[] = new int[]{1, 2, 3, 4, 5, 6};
//调用Arrays.stream返回数组
IntStream stream = Arrays.stream(arr);
Employee e1 = new Employee(1, "1", 1, 1);
Employee e2 = new Employee(2, "1", 1, 1);
Employee employees[] = new Employee[]{e1, e2};
Stream stream1 = Arrays.stream(employees);
}
/**
* 创建Stream方式3: 通过Stream的of()来创建
*/
@Test
public void test3() {
Stream integerStream = Stream.of(1, 2, 3, 4, 5, 6);
Stream extends Number> stream = Stream.of(1.0, 2, 3, 4, 5, 6);
}
/**
* 创建Stream方式4:创建无限流
*/
@Test
public void test4() {
//迭代
//遍历前10个偶数
Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::println);
//生成
Stream.generate(Math::random).limit(10).forEach(System.out::println);
}
}
package com.jiayifeng.java;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
/**
* author 爱编程的小贾
* create 2023-10-19 19:46
*
* 一:测试Stream的中间操作
*/
public class StreamAPITest1 {
// 1.筛选与切片
@Test
public void test1(){
List list = EmployeeData.getEmp();
// filter(Predicate p):接收Lambda,从流中欧排除某些元素
Stream stream = list.stream();
// 查询员工表中薪资大于7000的员工信息
stream.filter(e -> e.getSalary() > 7000).forEach(System.out :: println);
System.out.println();
// limit(n):截断流,使其元素不超过给定数量
list.stream().limit(3).forEach(System.out :: println);
System.out.println();
// skip(n):跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流
list.stream().skip(3).forEach(System.out :: println);
System.out.println();
// distinct():筛选,通过流所生成元素的hashCode()和equals()去除重复元素
list.add(new Employee(1010,"刘强东",40,8000));
list.add(new Employee(1010,"刘强东",40,8000));
list.add(new Employee(1010,"刘强东",40,8000));
list.add(new Employee(1010,"刘强东",40,8000));
list.add(new Employee(1010,"刘强东",40,8000));
// System.out.println(list);
list.stream().distinct().forEach(System.out :: println);
}
// 2.映射
@Test
public void test2(){
// map(Function f):接受一个函数作为参数,将元素转换成其它形式或提取信息,该函数会
// 被应用到每一个元素上,并将其映射成一个新的元素
List list = Arrays.asList("aa","bb","cc","dd");
list.stream().map(str -> str.toUpperCase()).forEach(System.out :: println);
// flatMap(Function f):接收一个函数作为参数,将流中的每一个值都换成另一个流,然后把所有
// 流连接成一个流
ArrayList list1 = new ArrayList<>();
list1.add(1);
list1.add(2);
list1.add(3);
ArrayList list2 = new ArrayList();
list2.add(4);
list2.add(5);
list2.add(6);
list1.addAll(list2);
System.out.println(list1);
}
// 3.排序
@Test
public void test3(){
// sort():自然排序
List list = Arrays.asList(12, 45, 24, 46, 67, 98);
list.stream().sorted().forEach(System.out :: println);
// 抛异常,原因:Employee没有实现Comparable接口
// List emp = EmployeeData.getEmp();
// emp.stream().sorted().forEach(System.out :: println);
// sorted(Compatater com):定制排序
List emp = EmployeeData.getEmp();
emp.stream().sorted((e1,e2) -> Integer.compare(e1.getAge(),e2.getAge()))
.forEach(System.out :: println);
}
}
package com.jiayifeng.java1;
/**
* author 爱编程的小贾
* create 2023-10-21 11:49
*/
public class Student {
private int id;//id
private String name;//姓名
private int age;//年龄
private Score score;//成绩(优秀、良好、及格)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Score getScore() {
return score;
}
public void setScore(Score score) {
this.score = score;
}
public enum Score{
EXCELLENT,
GOOD,
PASS
}
public Student(int id, String name, int age, Score score) {
this.id = id;
this.name = name;
this.age = age;
this.score = score;
}
}
package com.jiayifeng.java1;
/**
* author 爱编程的小贾
* create 2023-10-21 11:50
*/
import com.jiayifeng.java.Employee;
import com.jiayifeng.java.EmployeeData;
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @description: 终止操作
* allMatch——检查是否匹配所有元素
* anyMatch——检查是否至少匹配一个元素
* noneMatch——检查是否没有匹配的元素
* findFirst——返回第一个元素
* findAny——返回当前流中的任意元素
* count——返回流中元素的总个数
* max——返回流中最大值
* min——返回流中最小值
* @author: xz
*/
public class StreamTest {
public static void main(String[] args) {
List studentsList = Arrays.asList(
new Student(1, "李四", 20,Student.Score.EXCELLENT),
new Student(2, "张三", 19,Student.Score.GOOD ),
new Student(3, "王五", 24,Student.Score.PASS),
new Student(4, "赵六", 23, Student.Score.GOOD),
new Student(5, "xz", 21, Student.Score.PASS )
);
//检查学生的成绩是都是良好
boolean b = studentsList.stream()
.allMatch(s -> s.getScore().equals(Student.Score.GOOD));
System.out.println("allMatch——"+b);
//检查学生的成绩至少有一个是良好
boolean b1 = studentsList.stream()
.anyMatch(s -> s.getScore().equals(Student.Score.GOOD));
System.out.println("anyMatch——"+b1);
//检查学生的成绩是否没有匹配到良好
boolean b2 = studentsList.stream()
.noneMatch(s -> s.getScore().equals(Student.Score.GOOD));
System.out.println("noneMatch——"+b2);
//按年龄排序且返回第一个元素
Optional op1 = studentsList.stream()
.sorted((s1, s2) -> Integer.compare(s1.getAge(), s2.getAge()))
.findFirst();
System.out.println("findFirst(按年龄排序且返回第一个元素)——"+op1.get());
//查找学生成绩为及格且返回的任意元素
Optional op2 = studentsList.parallelStream()
.filter(s -> s.getScore().equals(Student.Score.PASS))
.findAny();
System.out.println("findAny(查找学生成绩为及格且返回的任意元素)——"+op2.get());
///查找学生的总人数
long count = studentsList.stream()
.count();
System.out.println("count——"+count);
//查找年龄最大的学生信息
Optional op3 = studentsList.stream()
.max((s1, s2) -> Integer.compare(s1.getAge(), s2.getAge()));
System.out.println("max——"+op3.get());
//查找学生所有的年龄范围,并获取最小的年龄
Optional op4 = studentsList.stream()
.map(Student::getAge)
.min(Integer::compare);
System.out.println("min——"+op4.get());
}
/*
allMatch——false
anyMatch——true
noneMatch——false
findFirst(按年龄排序且返回第一个元素)——com.jiayifeng.java1.Student@5f184fc6
findAny(查找学生成绩为及格且返回的任意元素)——com.jiayifeng.java1.Student@34a245ab
count——5
max——com.jiayifeng.java1.Student@34a245ab
min——19
*/
// 规约
@Test
public void Test1(){
// 练习一:计算1-10的自然数之和
List list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer sum = list.stream().reduce(0,Integer::sum);
System.out.println(sum);//55
// 练习二:计算公司所有员工工资的总和
List employees = EmployeeData.getEmp();
Stream salaryStream = employees.stream().map(Employee::getSalary);
Optional sumMoney = salaryStream.reduce((d1, d2) -> (d1 + d2));
System.out.println(sumMoney);
}
// 收集
@Test
public void test2(){
List emp = EmployeeData.getEmp();
List employeeList = emp.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
employeeList.forEach(System.out::println);
}
}
Stream的详细介绍参考:http://t.csdnimg.cn/bAycz
为了解决空指针异常而引入Optional类
Optional类的javadoc描述如下:这是一个可以为null的容器对象。如果值存在,则isPresent()方法会返回true,调用get()方法会返回该对象
package com.jiayifeng.java2;
/**
* author 爱编程的小贾
* create 2023-10-21 12:25
*/
public class Girl {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Girl() {
}
@Override
public String toString() {
return "Girl{" +
"name='" + name + '\'' +
'}';
}
public Girl(String name) {
this.name = name;
}
}
package com.jiayifeng.java2;
/**
* author 爱编程的小贾
* create 2023-10-21 12:25
*/
public class Boy {
private Girl girl;
public Boy(Girl girl) {
this.girl = girl;
}
public Boy() {
}
@Override
public String toString() {
return "Boy{" +
"girl=" + girl +
'}';
}
public Girl getGirl() {
return girl;
}
public void setGirl(Girl girl) {
this.girl = girl;
}
}
package com.jiayifeng.java2;
import org.junit.Test;
import java.util.Optional;
/**
* author 爱编程的小贾
* create 2023-10-21 12:27
*
* 一:Optional类:为了在程序中避免出现空指针异常而创建的
*/
public class OptionalTest {
@Test
public void test1(){
// 1.Optional.of(T t):创建一个Optional实例,t必须为空
Girl girl = new Girl();
Optional girl1 = Optional.of(girl);
// 2.Optional.empty():创建一个空的Optional实例
// 3.Optional.ofNullable(T t):t可以为空
Girl girl2 = new Girl();
girl2 = null;
Optional optionalGirlgirl1 = Optional.ofNullable(girl2);
System.out.println(optionalGirlgirl1);
}
public String getGirlName(Boy boy){
Optional boyOptional = Optional.ofNullable(boy);
// 此时的boy1一定非空
Boy boy1 = boyOptional.orElse(new Boy(new Girl("迪丽热巴")));
Girl girl1 = boy1.getGirl();
Optional girlOptional = Optional.ofNullable(girl1);
// girl2一定非空
Girl girl2 = girlOptional.orElse(new Girl("古力娜扎"));
return girl2.getName();
}
@Test
public void test2(){
Boy boy = null;
boy = new Boy();
boy = new Boy(new Girl("贾老师"));
String girlName = getGirlName(boy);
System.out.println(girlName);
}
}