1. Lambda
1.1 介绍
Lambda表达式是一种没有名字的函数,也可称为闭包,是Java 8 发布的最重要新特性。
本质上是一段匿名内部类,也可以是一段可以传递的代码。
还有叫箭头函数的...
闭包 :
闭包就是能够读取其他函数内部变量的函数,比如在java中,方法内部的局部变量只能在方法内部使用,所以闭包可以理解为定义在一个函数内部的函数
闭包的本质就是将函数内部和函数外部链接起来的桥梁
1.2 特点
允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
使用 Lambda 表达式可以使代码变的更加简洁紧凑。
1.3 和匿名内部类对比
1.4 应用场景
1. 列表迭代
2. Map映射
3. Reduce聚合
4. 代替一个不想命名的函数或是类,该函数或类往往并不复杂。
5. 想尽量缩短代码量的各个场景均可以
1.5 代码实现
1.5.1 具体语法
1、(parameters) -> expression
2、(parameters) ->{ statements; }
1.5.2 语法特点
可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值
如果只有一条语句,并且是返回值语句,就可以不写return 不写 {}
如果写上{} 就必须写return 和 ;
如果有 多条语句,必须写{} return 和 ; 也必须写
案例说明-简单
// 1. 不需要参数,返回值为 5
() -> 5
// 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2*x
// 3. 接受2个参数(数字),并返回他们的差值
(x, y) -> x – y
// 4. 接收2个int型整数,返回他们的和
(int x, int y) -> x + y
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> System.out.print(s)
1.5.3 集合遍历
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
public class _01_Lambda {
public static void main(String[] args) {
String[] arr = { "a", "b", "c" };
// 把数组转换为集合
List<String> strings = Arrays.asList(arr);
// 1.8之前的写法
for (String string : strings) {
System.out.println(string);
}
// 1.8开始后
// 匿名内部类
strings.forEach(new Consumer<String>() {
@Override
public void accept(String t) {
System.out.println(t);
}
});
// lambda
strings.forEach(x->System.out.println(x));
}
}
1.5.4 集合排序
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class _02_Lambda {
public static void main(String[] args) {
Integer[] arr = { 1, 3, 7, 5, 2, 8, 6 };
List<Integer> list = Arrays.asList(arr);
// 1.8之前
Collections.sort(list, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
System.out.println(list);
// 1.8 开始 lambda
list.sort((a, b) -> a - b);
System.out.println(list);
}
}
2. 函数式接口
2.1 介绍
英文称为Functional Interface
其本质是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。
核心目标是为了给Lambda表达式的使用提供更好的支持,进一步达到函数式编程的目标,可通过运用函数式编程极大地提高编程效率。
其可以被隐式转换为 lambda 表达式。
2.2 特点
函数式接口是仅制定一个抽象方法的接口
可以包含一个或多个静态或默认方法
专用注解即@FunctionalInterface 检查它是否是一个函数式接口,也可不添加该注解
如果有两个或以上 抽象方法,就不能当成函数式接口去使用,也不能添加@FunctionalInterface这个注解
如果只有一个抽象方法,那么@FunctionalInterface注解 加不加 都可以当做函数式接口去使用
回调函数 :
简单来说就是回调函数,方法的参数是一个方法,在这个方法中对传递的方法进行调用
2.3 应用场景
想通过函数式编程,提高编程效率的各种场景均可
2.4 代码实现
2.4.1 无参情况
public class _01_FunInterface {
public static void main(String[] args) {
// 1 实现类方式
m1(new Test());
// 2 匿名内部类
m1(new MyFunInterface() {
@Override
public void print() {
System.out.println("学习学习");
}
});
// 3 lambda 直接调用接口
m1(()->System.out.println("啛啛喳喳"));
}
public static void m1(MyFunInterface fun){
fun.print();
}
}
@FunctionalInterface
interface MyFunInterface {
void print();
}
//实现类方法要创建子实现类
class Test implements MyFunInterface {
@Override
public void print() {
System.out.println("我最帅");
}
}
2.4.2 有参情况
public class _02_FunInterface {
public static void main(String[] args) {
// 1 实现类方式
m1(new Test1(),"我最帅");
// 2 匿名内部类
m1(new MyFunInterface1() {
@Override
public void print(String msg) {
System.out.println(msg);
}
},"你也帅");
// 3 lambda
m1((x)->System.out.println(x),"cc");
// 4 创建函数对象方式
MyFunInterface1 obj = (x)->System.out.println(x);
obj.print("xxx");
}
public static void m1(MyFunInterface1 fun,String msg){
fun.print(msg);
}
}
@FunctionalInterface
interface MyFunInterface1 {
void print(String msg);
}
class Test1 implements MyFunInterface1 {
@Override
public void print(String msg) {
System.out.println(msg);
}
}
在方法4中 (x)->System.out.println(x) 代码属于匿名内部类的简写方式, 创建了匿名内部类的对象,直接调用 obj 的 print方法传参 可以省略m1 方法的调用
2.5 JDK自带常用的函数式接口
2.5.1 Supplier<T>接口
Supplier<T>接口 代表结果供应商,所以有返回值,可以获取数据
有一个get方法,用于获取数据
public class _03_JdkOwn_01 {
private static String getResult(Supplier<String> function) {
return function.get();
}
public static void main(String[] args) {
// 1
String before = "张三";
String after = "你好";
// 把两个字符串拼接起来
System.out.println(getResult(() -> before + after));
// 2 //创建Supplier容器,声明为_03_JdkOwn类型
// 此时并不会调用对象的构造方法,即不会创建对象
Supplier<_03_JdkOwn_01> sup = _03_JdkOwn_01::new;
_03_JdkOwn_01 jo1 = sup.get();
_03_JdkOwn_01 jo2 = sup.get();
}
public _03_JdkOwn_01() {
System.out.println("构造方法执行了");
}
}
2.5.2 Consumer<T>接口
Consumer<T>接口 消费者接口所以不需要返回值
有一个accept(T)方法,用于执行消费操作,可以对给定的参数T 做任意操作
public class _04_JdkOwn_02 {
private static void consumeResult(Consumer<String> function, String message) {
function.accept(message);
}
public static void main(String[] args) {
// 传递的参数
String message = "消费一些内容!!!";
// 调用方法
consumeResult(result -> {
System.out.println(result);
}, message);
}
}
2.5.3 Function<T,R>接口
Function<T,R>接口 表示接收一个参数并产生结果的函数
顾名思义,是函数操作的
有一个R apply(T)方法,Function中没有具体的操作,具体的操作需要我们去为它指定,因此apply具体返回的结果取决于传入的lambda表达式
public class _05_JdkOwn_03 {
// Function<参数, 返回值>
public static void convertType(Function<String, Integer> function,
String str) {
int num = function.apply(str);
System.out.println(num);
}
public static void main(String[] args) {
// 传递的参数
String str = "123";
// s是说明需要传递参数, 也可以写 (s)
convertType(s -> {
int sInt = Integer.parseInt(s);
return sInt;
}, str);
}
}
2.5.4 Predicate<T>接口
Predicate<T>接口 断言接口
就是做一些判断,返回值为boolean
有一个boolean test(T)方法,用于校验传入数据是否符合判断条件,返回boolean类型
public class _06_JdkOwn_04 {
// 自定义方法,并且 Predicate 接收String字符串类型
public static void call(Predicate<String> predicate, String isOKMessage) {
boolean isOK = predicate.test(isOKMessage);
System.out.println("isOK吗:" + isOK);
}
public static void main(String[] args) {
// 传入的参数
String input = "ok";
call((String message) -> {
// 不区分大小写比较,是ok就返回true,否则返回false
if (message.equalsIgnoreCase("ok")) {
return true;
}
return false;
}, input);
}
}
3. 方法引用和构造器的调用
3.1 概念说明
Lambda表达式的另外一种表现形式,提高方法复用率和灵活性。
3.2 特点
更简单、代码量更少、复用性、扩展性更高.
3.3 应用场景
若Lambda 体中的功能,已经有方法提供了实现,可以使用方法引用。
不需要再复写已有API的Lambda的实现。
3.4 代码实现
3.4.1 方法引用
方法引用-3种形式
3.4.1.1 对象的引用 :: 实例方法名
import java.util.function.Supplier;
/**
* 成员方法调用 : 对象引用::方法名
* 无参有返回值 用Supplier
*/
public class _01_FunCall {
public static void main(String[] args) {
Integer i1 = new Integer(22);
// 常规调用
int i2 = i1.intValue();
// lambda
Supplier<Integer> su = () -> i1.intValue();
System.out.println(su.get());
// 方法引用
Supplier<Integer> su1 = i1::intValue;
System.out.println(su1.get());
}
}
3.4.1.2 类名 :: 静态方法名
import java.util.function.BiFunction;
/**
* 静态方法调用 类名::静态方法
* 有参数有返回值 用Function
*/
public class _02_FunCall {
public static void main(String[] args) {
// lambda
BiFunction<Integer, Integer, Integer> bi = (x, y) -> Integer.max(x, y);
int max = bi.apply(12, 15);
System.out.println(max);
// 方法引用
BiFunction<Integer, Integer, Integer> b2 = Integer::max;
max = b2.apply(15, 18);
System.out.println(max);
}
}
3.4.1.3 类名 :: 成员方法名
import java.util.function.BiPredicate;
import java.util.function.Predicate;
/**
* 类名::成员方法名
*
*/
public class _03_FunCall {
public static void main(String[] args) {
//常规写法
BiPredicate<String,String> bp = (x,y)->x.equals(y);
boolean flag = bp.test("12", "12");
System.out.println(flag);
// 方法引用
BiPredicate<String,String> bp1 = String::equals;
flag = bp1.test("z", "z");
System.out.println(flag);
}
}
3.4.2 构造器调用
import java.util.function.Function;
import java.util.function.Supplier;
/**
* 构造器
*
*/
public class _04_FunCall {
public static void main(String[] args) {
Object o1 = new Object();
//无参有返回值
// lambda
Supplier<Object> su = () -> new Object();
System.out.println(su.get());
// 方法引用
Supplier<Object> su1 = Object::new;
System.out.println(su1.get());
//有参有返回值
// lambda
Function<Integer, Integer> fun = x -> new Integer(x);
System.out.println(fun.apply(15));
// 方法引用
Function<Integer, Integer> fun1 = Integer::new;
System.out.println(fun.apply(45));
}
}
3.4.3 数组调用
public static void main(String[] args) {
// lambda传统写法
Function<Integer, Integer[]> fun = (n) -> new Integer[n];
Integer[] intArray1 = fun.apply(5);
intArray1[0] = 100;
System.out.println(intArray1[0]);
// 数组引用新写法
Function<Integer, Integer[]> fun1 = Integer[]::new;
Integer[] intArray = fun1.apply(5);
intArray[0] = 100;
System.out.println(intArray[0]);
}
4. Stream API
4.1 概念说明
数据渠道、管道,用于操作数据源(集合、数组等)所生成的元素序列。
集合讲的是数据,流讲的是计算
即一组用来处理数组,集合的API。
4.2 特点
Stream 不是数据结构,没有内部存储,自己不会存储元素。
Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
不支持索引访问。
延迟计算
支持并行
很容易生成数据或集合
支持过滤,查找,转换,汇总,聚合等操作。
4.3 应用场景
流式计算处理,需要延迟计算、更方便的并行计算
更灵活、简洁的集合处理方式场景
4.4 代码实现
4.4.1 运行机制说明
Stream分为源source,中间操作,终止操作。
流的源可以是一个数组,集合,生成器方法,I/O通道等等。
一个流可以有零个或多个中间操作,每一个中间操作都会返回一个新的流,供下一个操作使用,一个流只会有一个终止操作。
中间操作也称为转换算子-transformation
Stream只有遇到终止操作,它的数据源会开始执行遍历操作。
终止操作也称为动作算子-action
因为动作算子的返回值不再是 stream,所以这个计算就终止了
只有碰到动作算子的时候,才会真正的计算
4.4.2 代码实现
4.4.2.1 生成stream流的5种方式说明
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
* 获取Stream的几种方式
*
*/
public class _01_Stream {
public static void main(String[] args) {
// 1 数组 Stream.of
String[] strings = { "q", "w", "e", "r", "t" };
Stream<String> stream1 = Stream.of(strings);
// 2 集合创建
List<String> strings2 = Arrays.asList(strings);
Stream<String> stream2 = strings2.stream();
// 3 生成器 Stream.generate
// 无限流,无限大,并且默认值都是1(因为我们写的是1)
// 可以通过limit 来指定最大个数
Stream<Integer> stream3 = Stream.generate(() -> 1);
stream3.limit(10).forEach(x -> System.out.println(x));
// 4 Stream.iterate
// 无限流,无限大
// 第一个参数是起始值,第二个参数 写的x+2 意味着 步长为2
// 数据为 1开始 步长为2依次递增 : 1,3,5,7,9,11,13,......
Stream<Integer> stream4 = Stream.iterate(1, x -> x + 2);
stream4.limit(10).forEach(x -> System.out.println(x));
// 5 已有的Stream原生API
String str = "abc";
IntStream is = str.chars();
is.forEach(x -> System.out.println(x));
}
}
4.4.2.2 常用转换算子
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 转换算子 : 转换算子不会执行,有动作算子之后才会执行,并且转换算子返回值都是新的Stream
*
*/
public class _02_Stream {
public static void main(String[] args) {
String[] arr = { "qasdas", "wdasdasd", "eddsa", "rwc", "tcsawe",
"tdsa", "qxxx" };
List<String> strings = Arrays.asList(arr);
Stream<String> stream = strings.stream();
/**
* filter : 对元素进行过滤筛选,不符合条件的就不要了
*
* 只要q , x -> x.equals("q") 如果返回true,就要,返回false 就不要
*
* collect 动作算子,把执行的结果转换为集合,并返回(Collectors.toList())
*/
List<String> result = stream.filter(x -> x.equals("q")).collect(
Collectors.toList());
// 流使用过之后要重新生成,否则会报错
stream = strings.stream();
/**
* skip 跳过几个元素
*/
result = stream.skip(2).collect(Collectors.toList());
/**
* distinct : 去重(去除重复元素)
*/
stream = strings.stream();
result = stream.distinct().collect(Collectors.toList());
/**
* limit : 取前几条
*/
stream = strings.stream();
result = stream.limit(3).collect(Collectors.toList());
/**
* map : 在遍历过程中,对数据进行修改并返回
*
* 比如 把英文名称首字母大写, 或者 所有公司员工涨薪10% , 或者 入职1年的员工涨薪10%
*/
stream = strings.stream();
result = stream.map(x -> x + "--").collect(Collectors.toList());
stream = strings.stream();
// result = stream.map(x->x.toLowerCase().substring(0,
// 1).toUpperCase()+x.toLowerCase().substring(1)).collect(Collectors.toList());
result = stream.map(x -> {
x = x.toLowerCase();
return x.substring(0, 1).toUpperCase() + x.substring(1);
}).collect(Collectors.toList());
System.out.println(result);
/**
* flatMap
*
* 把两个字符串,分别以特定符号进行分割,然后得到两个字符串数组,并把两个数组合并到一个集合中
*/
strings = Arrays.asList("1,2,3,4", "a,b,c,d");
stream = strings.stream();
result = stream.map(x -> x.split(","))
.flatMap(arr1 -> Arrays.stream(arr1))
.collect(Collectors.toList());
System.out.println(result.size());
/**
* sorted 排序
*/
List<Integer> list = Arrays.asList(1, 5, 2, 7, 3);
// list.sort((x,y)->y-x);
Stream<Integer> stream1 = list.stream();
List<Integer> result1 = stream1.sorted((x, y) -> y - x).collect(
Collectors.toList());
System.out.println(result1);
}
}
4.4.2.3 常用动作算子
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 常用动作算子
*
* 循环 : forEach
*
* 收集器 : collect
*
* 计算 : min,max,count,average
*
*/
public class _03_Stream {
public static void main(String[] args) {
List<String> strings = Arrays.asList("a", "b", "c", "a");
// forEach
Stream<String> stream = strings.stream();
stream.forEach(x -> System.out.println(x));
// count
stream = strings.stream();
// 只使用动作算子count,没有太多价值,还不如直接使用集合的size方法
// long count = stream.count();
// 一般需要结合中间算子,比如统计有多少个a
long count = stream.filter(x -> x.equals("a")).count();
System.out.println(count);
// collect
stream = strings.stream();
List<String> result = stream.filter(x->x.equals("a")).collect(Collectors.toList());
System.out.println(result);
// 最大最小
List<Integer> integers = Arrays.asList(1,2,3,4,6,7);
Stream<Integer> stream2 = integers.stream();
System.out.println(stream2.min((x,y)->x-y).get());
// anyMatch 匹配,是否包含
stream2 = integers.stream();
System.out.println(stream2.anyMatch(x->x==3));
// 这种的一般使用contains即可解决,但是很多是contains解决不了的
System.out.println(integers.contains(3));
List<User> users = new ArrayList<User>();
User u1 = new User("张三", 18);
User u2 = new User("李四", 19);
User u3 = new User("李四", 19);
users.add(u1);
users.add(u2);
System.out.println(users.contains(u3));
Stream<User> stream3 = users.stream();
System.out.println(stream3.anyMatch(x->x.getAge() == 19 && x.getName().equals("李四") ));
}
}
class User{
private String name;
private int age;
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 User(String name, int age) {
super();
this.name = name;
this.age = age;
}
}
5. 接口中的默认方法和静态方法
5.1 概念说明
1.8之前接口中只能定义public static final的变量和public abstract修饰的抽象方法。
1.8及以后版本,不仅兼容1.8以前的,并新增了默认方法定义和静态方法定义的功能。
即default方法和static方法。
让接口更灵活、更多变,更能够适应现实开发需要。
5.2 特点
默认方法 :
可以被重写,也可以不重写。如果重写的话,就按实现类的方法来执行。
调用的时候必须是实例化对象调用。
静态方法 :
跟之前的普通类的静态方法大体相同
唯一不同的是不能通过接口的实现类的对象来调用,必须是类.静态方法的方式。
5.3 应用场景
默认方法
是为了解决之前版本接口升级导致的大批实现类的强制重写方法升级的问题。
涉及到接口升级的场景可以多用,从而提高了程序的扩展性、可维护性。
静态方法
跟默认方法为类似,也是为了解决接口升级的问题,默认方法解决了接口方法增加后其子类不必要全部重写的问题,静态方法解决了一次编写静态方法所有接口及其子类通用的问题,跟lambda表达式并用效果更加,进一步提高了程序扩展性和可维护性。
允许在已有的接口中添加静态方法,接口的静态方法属于接口本身,不被继承,也需要提供方法的静态实现后,子类才可以调用。
5.4 代码实现
5.4.1 默认方法
public class _01_Interface {
public static void main(String[] args) {
MyInter inter = new MyInterImpl();
System.out.println(inter.add(3, 4));
inter.printMessage();
}
}
interface MyInter {
// 之前的用法
int add(int i, int j);
// 新加的默认方法
default void printMessage() {
System.out.println("在接口中的默认实现");
}
}
class MyInterImpl implements MyInter {
@Override
public int add(int i, int j) {
return i + j;
}
@Override
public void printMessage() {
System.out.println("在接口的实现类中的默认实现");
}
}
5.4.2 静态方法案例-1-一般情况
类名调用就行,各是各的,互不影响
public class _02_Interface {
public static void main(String[] args) {
Person.run();
Man.run();
}
}
interface Person {
public static void run() {
System.out.println("人类都可以跑!!!");
}
}
class Man implements Person {
public static void run() {
System.out.println("人类都可以跑,男人跑的更快!!!");
}
}
5.4.3 静态方法案例-2-高级使用
类名调用就行,各是各的,互不影响
public class _03_Interface {
public static void main(String[] args) {
Person_01 m1 = PersonFactory.create(Man_01::new);
m1.say();
}
}
interface PersonFactory {
public static Person_01 create(Supplier<Person_01> supplier) {
return supplier.get();
}
}
interface Person_01 {
default void say() {
System.out.println("人类会说话!!!");
}
public static void run() {
System.out.println("人类都可以跑!!!");
}
}
class Man_01 implements Person_01 {
@Override
public void say() {
System.out.println("人类会说话,男人说话更粗糙!!!");
}
public static void run() {
System.out.println("人类都可以跑,男人跑的更快!!!");
}
}