大处着眼,小处着手。
(视频:尚硅谷:https://www.bilibili.com/video/BV1Kb411W75N?p=666)
目录
三:函数式(Functional)接口(Interface)
四:方法引用 与 构造器引用(用于替换 Lambda 表达式)
一:前言:
1)Java 8 最大的两个改变(2):
a:Lambda 表达式;
b:Stream API(并行流 / 串行流)
2)接口增强(2):接口中 可以写 (默认)default 方法,静态方法。
(java 8 中,Collection,List,Comparator 等接口,提供了丰富的默认方法。)
a:默认方法:由子类的对象调用,子类可以重写。
b:静态方法:由父类.静态方法名 调用,子类不能重写,子类对象不能调用。
public class Test_01 implements A {
@Override
public void a() {
System.out.println("重写后的A");
}
public static void main(String[] args) {
Test_01 test_01 = new Test_01();
test_01.a();
A.b();
//重写后的 A
//静态方法 b
}
}
interface A {
public default void a() {
System.out.println("重写前的 a");
}
public static void b() {
System.out.println("静态方法 b");
}
}
3)提供新的 时间 和 日期 API:(LocalDateTime();)
4)注解:(看 基础-注解 笔记)
a:可重复注解:
b:类型注解:
5)容器底层变化:集合和map。
6)方法引用:
7)构造器引用 / 数组引用:
8)函数式接口:@FunctionalInterface
9)Optional 类:
二:Lambda 表达式(使代码更简洁)
1)lambda介绍:
a:lambda 是一个匿名函数,我们可以将 Lambda 表达式,理解为是一段可以传递的代码,(将代码像数据一样传递)
b:使用它,可以写出更简洁,更灵活的代码。
2)将来大数据 spark 底层有使用,java8 新特性,比如 Lambda 表达式。
3)举例:
a:普通写法:
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("我爱北京天安门");
}
};
runnable.run();
Lambda表达式:
Runnable runnable2 = () -> System.out.println("我爱北京故宫");
runnable2.run();
b:普通写法:
Comparator<Integer> integerComparator = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
int compare = Integer.compare(o1, o2);
return compare;
}
};
int compare = integerComparator.compare(3, 2);
System.out.println(compare);
Lambda表达式写法:
Comparator<Integer> integerComparato2r = (o1, o2) -> Integer.compare(o1, o2);
int compare2 = integerComparato2r.compare(3, 2);
System.out.println(compare2);
方法引用写法:
Comparator<Integer> integerComparator1 = Integer::compare;
4)解释 lambda 表达式:
a:-> :Lambda 操作符/箭头操作符
b:左边:Lambda 形参列表
-1.接口中,抽象方法的形参。其实就是,接口中,抽象方法的形参列表。
-2.Lambda 表达式,参数类型可以省略。如果 Lambda 表达式 只有一个参数,其 () 也可以省略。
-3.右边:Lambda 体:
1.接口中,抽象方法的方法体。其实就是 抽象方法的 方法体。
2.Lambda 体,应该使用 { } 包裹,如果 Lambda 表达式,只有一条语句
(也有可能是 return 语句),则可以 省略 大括号 和 return 关键字。
c:以前用,匿名实现类 表示的方式 ,现在都可以用 Lambda 表达式来写。
d:Lambda 表达式 本质:就是一个函数式接口的实例。
5)Lambda 表达式分类(6)
a:无参 无返回值:
Runnable runnable = () -> System.out.println("123");
b:有参 无返回值:
Consumer<String> con = (String s) -> {
System.out.println("ss");
};
con.accept("张三");
c:数据类型可以省略,因为可以由编译器 推断得出,称为 “类型推断”
Consumer<String> con = (s) -> {
System.out.println("ss");
};
con.accept("张三");
d:Lambda 表达式,若只需要一个参数,参数的小括号可以省略:
Consumer<String> con = s -> {
System.out.println("ss");
};
con.accept("张三");
e:Lambda 表达式,需要两个或者以上的参数,多条语句执行,可以有返回值
Comparator<Integer> integerComparator = (o1, o2) -> {
System.out.println(o1);
System.out.println(o2);
return Integer.compare(o1, o2);
};
f:Lambda 体只有一条语句时,return 或 大括号 都可以可以省略:
Comparator<Integer> integerComparator = (o1, o2) -> return Integer.compare(o1, o2);
6)Lambda 表达式 本质:
a:作为 函数式 接口的实例,
b:函数式 接口 的特点就是,只有一个抽象方法。
-1.如果在一个接口中,只声明一个抽象方法,此接口 称为 函数式接口。
-2.接口使用 : @FunctionalInterface 修饰。
c:Lmabda 表达式 只能在 函数式接口中 使用。
三:函数式(Functional)接口(Interface)
1)如果一个接口中,只包含 一个抽象方法,此接口,就称为,函数式接口。
2)可以通过 Lambda 表达式,创建 函数式接口 的对象。
3)@FunctionalInterface 注解 :接口上标注,说明此接口,是函数事接口,
a:不是函数式接口,报错,也可以起到检验是否是函数式接口的作用。
4)Java 内置 ,四大核心 函数式接口:(参数类型 返回类型 用途)
a:消费型接口:放一个参数,但是不返回。
Consumer<T> T void void accep(T t)。对类型为 T 的对象 应用操作。
@Test
public void test_01() {
happyTime(22.2, new Consumer<Double>() {
@Override
public void accept(Double aDouble) {
System.out.println("学习太累了,去花 " + aDouble + " 钱。");
}
});
System.out.println("***********");
happyTime(22.2, a1Double -> System.out.println("学习太累了,去花 " + a1Double + " 钱。"));
}
public void happyTime(Double dd, Consumer<Double> consumer) {
consumer.accept(dd);
}
public static void main(String[] args) {
TestConsumer.TestCon(22.2, aDouble -> {
System.out.println(aDouble);
});
}
public static void TestCon(double dd, Consumer<Double> consumer) {
consumer.accept(dd);
}
b:供给型接口:不给他参数,他都往回返。
Supplier<T> 无 T T get()。 返回类型为 T 类型,包含方法:
Supplier<String> stringSupplier = () -> employee.getName();
String s = stringSupplier.get();
System.out.println(s);
c:函数型接口:放一个参数,返回一个参数。
Function<T,R> T R R apple(T t)。 对 类型 T 的对象应用操作,并返回 R 类型对象。
Function<Double, Long> doubleLongFunction = o -> Math.round(o);
Long apply = doubleLongFunction.apply(5.5);
System.out.println(apply);
Function<Integer, Employee> integerEmployeeFunction = o -> new Employee(o);
Employee apply = integerEmployeeFunction.apply(19);
System.out.println(apply);
d:断定型接口:放进一个参数,返回一个判断结果,(true/false)
Predicate<T> T Boolean boolean test(T t)。确定类型为 T 的对象,是否满足约束。
@Test
public void test_01() {
List<String> strings = Arrays.asList("北京", "南京", "天津", "东京");
List<String> j = filterString(strings, new Predicate<String>() {
@Override
public boolean test(String s) {
return s.contains("京");
}
});
System.out.println(j.toString());
}
public List<String> filterString(List<String> list, Predicate<String> predicate) {
ArrayList<String> filterList = new ArrayList<>();
for (String str : list) {
if (predicate.test(str)) {
filterList.add(str);
}
}
return filterList;
}
四:方法引用、构造器引用、数组引用 (用于替换 Lambda 表达式)
1)方法引用:
a:方法引用使用场景:
-1.当要传递给 Lambda体 的操作,已经有实现的方法了,可以使用方法引用。
-2.方法引用:可以看做是,Lambda 表达式,更深层次的表达。
-3.换句话说:方法引用本质 就是 Lambda 表达式,也就是函数式接口的一个实例。
-4.通过 方法的名字,来指向一个方法,可以认为是 Lambda 表达式 的一个语法糖。
b:要求:
b:使用格式:
-1.类 / 对象 :: 方法名
c:具体分为 三种情况:
-1.对象 :: 非静态方法:举例
1.Lambda表达式 表示:
Consumer<String> stringConsumer = o -> System.out.println(o);
stringConsumer.accept("张三");
方法引用 表示:
PrintStream stream = System.out;
//对象 :: 方法名
Consumer<String> stringConsumer2 = stream::println;
stringConsumer2.accept("张三");
2.Lambda表达式 表示:
Supplier<String> stringSupplier = () -> employee.getName();
String s = stringSupplier.get();
System.out.println(s);
方法引用 表示:
Supplier<String> stringSupplier2 = employee::getName;
String s1 = stringSupplier.get();
System.out.println(s1);
-2.类 :: 静态方法
1.Lambda表达式 表示:
Comparator<Integer> integerComparator = (o1, o2) -> Integer.compare(o1, o2);
int compare = integerComparator.compare(10, 20);
System.out.println(compare);
方法引用 表示:
Comparator<Integer> integerComparator1 = Integer::compare;
int compare1 = integerComparator1.compare(10, 20);
System.out.println(compare1);
2.Lambda表达式 表示:
Function<Double, Long> doubleLongFunction = o -> Math.round(o);
Long apply = doubleLongFunction.apply(5.5);
System.out.println(apply);
方法引用 表示:
Function<Double, Long> doubleLongFunction2 = Math::round;
Long apply2 = doubleLongFunction.apply(5.5);
System.out.println(apply2);
-3.类 :: 非静态方法
d:方法引用的使用要求:
-1.要求接口中的 抽象方法的 形参列表 和 返回值类型,与 方法引用的 方法的 形参列表 和 返回值类型相同。
4)构造器引用
d:构造器引用举例:
-1.Lambda表达式 表示:
Supplier<Employee> employeeSupplier = () -> new Employee();
Employee employee = employeeSupplier.get();
System.out.println(employee);
构造器引用 表示:
Supplier<Employee> employeeSupplier1 = Employee::new;
Employee employee1 = employeeSupplier.get();
System.out.println(employee);
-2.Lambda表达式 表示:
Function<Integer, Employee> integerEmployeeFunction = o -> new Employee(o);
Employee apply = integerEmployeeFunction.apply(19);
System.out.println(apply);
构造器引用 表示:
Function<Integer, Employee> integerEmployeeFunction2 = Employee::new;
Employee apply1 = integerEmployeeFunction2.apply(18);
System.out.println(apply1);
d:要求:
-1.和方法引用类似:
函数式接口的 抽象方法的 形参列表 和 构造器形参列表一致,
-2.抽象方法的返回值类型 即为 构造器 所属的类的类型。
4)数组引用:
a:介绍:如果把 数组 看做成为 一个 特殊的类,则写法 与 构造器引用 一致。
b:数组引用举例:
-1.Lambda表达式 表示:
Function<Integer, String[]> integerFunction = length -> new String[length];
String[] apply = integerFunction.apply(10);
for (String str : apply) {
System.out.print(str+",");
}
方法引用 表示:
Function<Integer, String[]> integerFunction1 = String[]::new;
String[] apply1 = integerFunction1.apply(3);
for (String str : apply1) {
System.out.print(str+",");
}
五:强大的 Stream API(对内存中数据操作)
1)Strean 介绍
a:并行流:
-1.就是将一个内容,分成多个数据块,并用不同的线程,分别处理每个数据块的流。
-2.相比于串行流,并行流可以很大程度上提高程序的执行效率。
b:串行流:
2)Stream 介绍
a:java 8 重要的改变:
-1.Stream API 真正的把 函数式编程风格,引入到java 中,简化了对集合的操作,
-2.可以执行复杂的 查找、过滤 和 映射数据 等操作。
b:为什么使用 Stream API:?
-1.实际开发中,项目中多数数据源都来自 mysql,oracle等数据库,
-2.但现在,数据源更多了,有 MongDB,Redis等,
-3.而这些,NoSQL 的数据,就需要java 层面来处理了。
c:Stream 和 Collection 区别:
-1.集合讲的数据的存储,与内存打交道。
-1.Stream 讲的是计算,与cpu打交道。
-2.Collection :
1.是一种 静态的内存数据结构,主要是对数据的存储。
2.主要是面向内存,存储到内存中。
-2.而 Stream :(数据渠道)
1.是有关计算的,用于操作数据源 所生成的 元素序列。
2.主要面向 CPU,通过 CPU 计算实现。
d:注意:
-1.Stream 不会自己存储数据
-2.Stream 不会改变源对象。相反,他们会返回一个,持有结果的新 Stream
-3.Stream 操作是延迟执行的。这意味着,他们会等到需要结果的时候,才运行。
3)Stream 操作有三个步骤:
a:创建 Stream:
-1.根据 一个数据源(如:集合,数组),获取一个 对应的流。
b:中间操作:
-1.一个中间操作链,对数据源的数据,进行处理。
c:终止操作(终端操作)
-1.一旦执行 终止操作,才会执行 中间操作链 ,并产生结果,之后不会在被使用。
4)Stream 创建对象操作(4种创建方式)
a:通过集合创建:
//获取 集合
List<Employee> employee = EmployeeData.getEmployee();
//顺序流:按照 集合中 的顺序
Stream<Employee> stream = employee.stream();
//并行流:不按照集合的顺序,在集合中并行取数据
Stream<Employee> employeeStream = employee.parallelStream();
b:通过数组创建 Stream
//创建 数组
String[] strings = {"张三", "李四", "王五"};
Stream<String> stream = Arrays.stream(strings);
c:通过 Stream 的 of
// 1
Stream<Integer> integerStream = Stream.of(1, 2, 32, 32, 34);
// 2
Stream<String> fdsf = Stream.of("fdsf", "afdsf", "sadf");
d:创建无限流,可以造数据。
-1.示例 1:
// Stream.iterate(1, new UnaryOperator<Integer>() {
// @Override
// public Integer apply(Integer integer) {
// return integer + 2;
// }
// });
//使用 Lambda 创建,取 10 个数,
Stream<Integer> iterate = Stream.iterate(1, integer -> integer + 2).limit(10);
//终止操作。
iterate.forEach(System.out::println);
5)Stream 的中间操作(3):
多个中间操作,可以连接起来一个链,形成一个流水线。
除非流水线上触发终止操作,否则中间操作不会执行任何处理!
而在终止操作时,一次性全部处理,称为 “惰性求值”。
a:筛选与切片
-1.过滤:filter(Predicate p):接收Lambda,从流中排除数据
List<Employee> employee = EmployeeData.getEmployee();
Stream<Employee> stream = employee.stream();
// Stream<Employee> employeeStream = stream.filter(new Predicate<Employee>() {
// @Override
// public boolean test(Employee employee) {
// return employee.getSalary() > 7000;
// }
// });
Stream<Employee> employeeStream = stream.filter( employee1 -> employee1.getSalary() > 7000);
employeeStream.forEach(System.out::print);
-2.limit(int a):截断流:使其元素不会超过给定数量
List<Employee> employee = EmployeeData.getEmployee();
Stream<Employee> stream = employee.stream();
Stream<Employee> employeeStream = stream.filter(employee1 -> employee1.getSalary() > 700);
// 执行 截断
Stream<Employee> limit = employeeStream.limit(2);
limit.forEach(System.out::println);
-3.skip(n):跳过元素:返回一个扔掉了,跳过前 n 个元素的流。若超过,则没结果。
List<Employee> employee = EmployeeData.getEmployee();
Stream<Employee> stream = employee.stream();
Stream<Employee> employeeStream = stream.filter(employee1 -> employee1.getSalary() > 700);
// 跳过 前两个 结果
Stream<Employee> skip = employeeStream.skip(2);
skip.forEach(System.out::println);
-4.distinct():筛选:通过流 所生成 元素的 hashcode() 和 equals() 去除重复元素
List<Employee> employee = EmployeeData.getEmployee();
employee.add(new Employee(1003, "刘强东", 44, 234));
employee.add(new Employee(1003, "刘强东", 44, 234));
employee.add(new Employee(1003, "刘强东", 44, 234));
Stream<Employee> stream = employee.stream();
Stream<Employee> distinct = stream.distinct();
distinct.forEach(System.out::println);
b:映射:
-1.Map(Function f):接收一个函数,作为参数。将元素转换成 其他形式或提取信息,
该函数,将会被应用到 每个元素上,并将其映射成一个新的元素。
可以 将 list 里面的元素,挑选出来,转换为list<元素>的集合。
1.例:将 list 里面字母,转换为大写
List<String> strings = Arrays.asList("aa", "bb", "cc", "dd");
// strings.stream().map(new Function<String, String>() {
// @Override
// public String apply(String s) {
// return s.toUpperCase();
// }
// });
strings.stream().map(s -> s.toUpperCase()).forEach(System.out::println);
2.例:获取员工姓名长度 大于等于 3 的员工姓名
List<Employee> employee = EmployeeData.getEmployee();
//使用 map() 映射出名字
Stream<String> stringStream = employee.stream().map(Employee::getName);
//过滤 名字长度 大于等于 3 的人
Stream<String> stringStream1 = stringStream.filter(s -> s.length() >= 3);
stringStream1.forEach(System.out::println);
3.例:使用 方法引用:
public static void main(String[] args) {
List<Person> list = GetList.getList();
List<String> collect = list.stream()
.map(Person::getUsername
).filter((s) -> {
return s.length() > 3;
}).collect(Collectors.toList());
System.out.println(collect);
}
-2.flatMap(Function f):
接收一个函数作为参数,将流中的每个值,都换成另一个流。
然后把,所有流,连成一个流。(相当于 List 中 addAll() 方法 )
1.
2.
3.
c:排序:
-1.sorted():
a.产生一个 新流,其中 按照自然顺序 排序。
b.示例:
List<Integer> integers = Arrays.asList(123, 23122, 3, 123);
Stream<Integer> sorted = integers.stream().sorted();
sorted.forEach(System.out::println);
-2.sorted(Comparaotr com):
a.产生一个新流,其中按比较器 顺序排序。
b.示例:
List<Employee> employee = EmployeeData.getEmployee();
//根据年龄进行排序
Stream<Employee> sorted = employee.stream().sorted((o1, o2) -> Integer.compare(o1.getAge(), o2.getAge()));
sorted.forEach(System.out::println);
c.如果排序相同,可以按照第二个字段(工资)排序:
List<Employee> employee = EmployeeData.getEmployee();
Stream<Employee> sorted = employee.stream().sorted(new Comparator<Employee>() {
@Override
public int compare(Employee o1, Employee o2) {
int compare = Integer.compare(o1.getAge(), o2.getAge());
if (compare != 0) {
return compare;
} else {
return Double.compare(o1.getSalary(), o2.getSalary());
}
}
});
sorted.forEach(System.out::println);
4)终止操作:
a:匹配与查找:
-1.判断 是否都
//判断 流 里的对象的 年龄 是否都大于 18 岁
List<Employee> employees = EmployeeData.getEmployee();
Stream<Employee> stream = employees.stream();
// boolean b = stream.allMatch(new Predicate<Employee>() {
// @Override
// public boolean test(Employee employee) {
// return employee.getAge() > 18;
// }
// });
boolean b1 = stream.allMatch(employee -> employee.getAge() > 18);
System.out.println(b1);
-2.检查是否至少匹配一个(查看是否存在)
List<Employee> employees = EmployeeData.getEmployee();
Stream<Employee> stream = employees.stream();
//判断 是否存在 年龄 小于 33
boolean b1 = stream.anyMatch(e -> e.getAge() < 33);
System.out.println(b1);
-3.检查是否 没有匹配元素(没有匹配元素 为 true,有则为 false)
List<Employee> employees = EmployeeData.getEmployee();
Stream<Employee> stream = employees.stream();
//判断 是否存在 年龄 小于 33
boolean b = stream.noneMatch(employee -> employee.getName().startsWith("马"));
System.out.println(b);
-4.返回第一个元素
List<Employee> employees = EmployeeData.getEmployee();
Stream<Employee> stream = employees.stream();
Optional<Employee> first = stream.findFirst();
-5.返回任意一个元素
List<Employee> employees = EmployeeData.getEmployee();
// 获取 并行流
Stream<Employee> stream = employees.parallelStream();
Optional<Employee> first = stream.findAny();
-6.求流中 元素总个数
List<Employee> employees = EmployeeData.getEmployee();
Stream<Employee> stream = employees.parallelStream();
// 统计 年龄 大于 40 的人 的个数
long count = stream.filter(employee -> employee.getAge() > 40).count();
System.out.println(count);
-7.查找 流中 最大值、最小值
List<Employee> employees = EmployeeData.getEmployee();
Stream<Employee> stream = employees.parallelStream();
// 查询 工资最高的人
Optional<Employee> max = stream.max((o1, o2) -> Double.compare(o1.getSalary(), o2.getSalary()));
System.out.println(max);
-8.内部迭代(对于流的终止操作)(iterrater 指针,叫外部迭代)
List<Employee> employees = EmployeeData.getEmployee();
Stream<Employee> stream = employees.parallelStream();
//终止操作 --内部迭代
stream.forEach(System.out::println);
b:规约:
-1.reduce():可以将 流中的元素 反复结合起来,得到一个值,返回。
1.计算 1-10 自然数的和:
//计算 1-10 自然数的和
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
//原始方式
// Optional<Integer> reduce = Optional.ofNullable(list.stream().reduce(0, new BinaryOperator<Integer>() {
// @Override
// public Integer apply(Integer integer, Integer integer2) {
// return Integer.sum(integer, integer2);
// }
// }));
//lambda 表达式
// Optional<Integer> reduce =
// Optional.ofNullable(list.stream().reduce(0, (integer, integer2) -> Integer.sum(integer, integer2)));
//
Optional<Integer> reduce =
Optional.ofNullable(list.stream().reduce(0, Integer::sum));
System.out.println(reduce);
2.求 所有员工 工资和:
List<Employee> employee = EmployeeData.getEmployee();
Optional<Double> reduce = employee.stream().map(Employee::getSalary).reduce(Double::sum);
System.out.println(reduce);
或者:
List<Employee> employee = EmployeeData.getEmployee();
Optional<Double> reduce = employee.stream().map(Employee::getSalary).reduce((d1, d2) -> d1 + d2);
System.out.println(reduce);
c:收集
-1.collect(Collector c):收集
将 流 转换为 其他形式,收集 一个 Collector 接口的实现,用于给 Stream 中 元素做汇总的方法。
Collector 接口中,方法的实现 决定了如何对流执行收集的操作(如:收集到 List,Set,Map)。
另外,Collectors 类,提供了很多静态方法,可以方便的创建,常见收集器实例。
-2.将 过滤后的流,转换为 List 集合:
List<Employee> employee = EmployeeData.getEmployee();
List<Employee> collect = employee.stream().collect(Collectors.toList());
// for (Employee e : collect) {
// System.out.println(e.toString());
// }
collect.forEach(System.out::println);
六:Optional 类(最大化减少空指针异常)
1)Optional 类介绍:
a:Optional :是一个容器类,可以避免 空指针异常。
他可以保存 类型 T 的值,代表这个值存在。这是一个 可以为 Null 的容器对象
如果 值存在 ,调用 方法 会返回 true,调用 get() 方法 ,返回该对象 。
2)常用方法:
-1.Optional.of(T t) : 创建一个 Optional 实例,t 必须为 非空(不能为空)。
Stu stu = new Stu();
//stu = null;
Optional<Stu> stu1 = Optional.of(stu);
System.out.println(stu1);
-2.Optional.ofNullable( T t ) : 创建一个 Optional 实例,t 可以为空。
Stu stu = new Stu();
//stu = null;
Optional<Stu> stu1 = Optional.ofNullable(stu);
System.out.println(stu1);
-3.orElse(T t):