Lambda表达式
为什么使用Lambda 表达式
Lambda 是一个匿名函数,我们可以把Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
匿名内部类转换为lambda写法注意事项 :
- 一个参数 可以不加 ()
- 无参或者 多个参数 必须加() , 多个使用逗号隔开
- 参数可以指定类型也可以不指定
- 如果只有一条语句,大括号可以省略,并且语句不需要 ; 分号结尾
- 如果省略大括号,如果该语句是返回值语句,return也要省略
- 如果是多条语句 必须加{}, 如果有返回值 必须加return(加上大括号,方法体怎么写,这里就怎么写)
String[] arr = { "one", "two", "three" };
List<String> list = Arrays.asList(arr);
// 1.8之前写法
for (String string : list) {
System.out.println(string);
}
// 1.8开始使用lambda表达式
// 匿名内部类
// 无法退出循环
list.forEach(new Consumer<String>() {
@Override
public void accept(String t) {
System.out.println(t);
}
});
// lambda表达式,就是匿名内部类的一种简写方式
list.forEach(x -> {
System.out.println(x);
});
Lambda 表达式:在Java 8 语言中引入的一种新的语法元素和操作符。这个操作符为“->” ,该操作符被称为Lambda 操作符或箭头操作符。它将Lambda 分为两个部分:
左侧:指定了Lambda 表达式需要的参数列表
右侧:指定了Lambda 体,是抽象方法的实现逻辑,也即Lambda 表达式要执行的功能。
Integer[] arr = { 1, 5, 9, 2, 4, 7 };
List<Integer> list = Arrays.asList(arr);
// 1.8之前的排序API
Collections.sort(list, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
// 1.8开始 新方式,sort方法,不再依赖Collections的API
list.sort(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
// 1.8开始 使用lambda表达式,也叫箭头函数
list.sort((x,y)->x-y);
System.out.println(list);
类型推断
上述Lambda 表达式中的参数类型都是由编译器推断得出的。Lambda 表达式中无需指定类型,程序依然可以编译,这是因为javac根据程序的上下文,在后台推断出了参数的类型。Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的“类型推断”。
函数式(Function)接口
只包含一个抽象方法的接口,称为函数式接口。
我们可以在一个接口上使用**@FunctionalInterface**注解,这样做可以检查它是否是一个函数式接口。同时javadoc也会包含一条声明,说明这个接口是一个函数式接口。
在java.util.function包下定义了Java 8 的丰富的函数式接口
所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。
作为参数传递Lambda 表达式:为了将Lambda 表达式作为参数传递,接收Lambda 表达式的参数类型必须是与该Lambda 表达式兼容的函数式接口的类型。
public class FunInterface_01 {
public static void main(String[] args) {
// 匿名内部类写法
test(new MyFunctionInter() {
public void printMessage() {
System.out.println("匿名内部类");
}
});
// lambda写法 第一种,调用自定义的test方法,并传递
test(() -> {
System.out.println("lambda");
});
// lambda写法第二种 创建函数对象,调用方法
MyFunctionInter inter = ()->{
System.out.println("lambda2");
};
inter.printMessage();
}
public static void test(MyFunctionInter a) {
a.printMessage();
}
}
@FunctionalInterface
interface MyFunctionInter {
public void printMessage();
}
方法引用与构造器引用
方法引用
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用就是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为是Lambda表达式的一个语法糖。
要求:实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致!
格式:使用操作符“::” 将类(或对象) 与方法名分隔开来。
如下三种主要使用情况:
- 对象::实例方法名
- 类::静态方法名
- 类::实例方法名
- 对象::成员方法
也就是说,调用方法语法,基本都一样, :: ,主要就是要知道
你想要调用的这个方法的入参和出参是什么,根据这个入参和出参去选择对应的函数式接口类型来接收这个值即可
对象::成员方法名
Integer intObj = new Integer(123);
intObj.toString();
// lambda
Supplier<String> sup = () -> intObj.toString();
System.out.println(sup.get());
// 方法引用
Supplier<String> sup1 = intObj::toString;
System.out.println(sup1.get());
类名::静态方法名
// 前两个是参数,第三个是返回值
// lambda写法
BiFunction<Integer, Integer, Integer> bi = (x,y)->Integer.max(x, y);
System.out.println(bi.apply(10, 22));
// 方法引用
BiFunction<Integer, Integer, Integer> bi1 = Integer::max;
System.out.println(bi1.apply(222, 1));
类名::成员方法
// 常规写法
BiPredicate<String, String> bi1 = (x,y)-> x.equals(y);
System.out.println(bi1.test("a", "a1"));
// 方法引用
BiPredicate<String, String> bi2 = String::equals;
System.out.println(bi2.test("a1", new String("a1")));
构造器引用
// 无参构造
// lambda写法
Supplier sup1 = () -> new Object();
System.out.println(sup1.get());
System.out.println(sup1.get());
// 方法引用
Supplier sup2 = Object::new;
System.out.println(sup2.get());
System.out.println(sup2.get());
// 有参
// lambda写法
Function<String, Integer> fun1 = (x)->new Integer(x);
System.out.println(fun1.apply("123")+23);
// 方法引用
Function<String, Integer> fun2 = Integer::new;
System.out.println(fun1.apply("123")+23);
// lambda
Function<Integer, Integer[]> fun1 = n->new Integer[n];
Integer[] arr1 = fun1.apply(10);
// 数组引用
Function<Integer, Integer[]> fun2 = Integer[]::new;
Integer[] arr2 = fun2.apply(10);
JDK中的函数式接口
Supplier 接口 代表供应商,方法有返回值,一般用于获取数据
get方法
public JDKOwn_01(){
System.out.println("构造方法");
}
public static void main(String[] args) {
String result = getResult(()->"Hello");
System.out.println(result);
// 使用Supplier创建对象
// 当调用get方法的时候,才会创建对象
Supplier<JDKOwn_01> sup = JDKOwn_01::new;
JDKOwn_01 o1 = sup.get();
JDKOwn_01 o2 = sup.get();
}
public static String getResult(Supplier<String> function){
return function.get();
}
Consumer 接口 消费者接口,不需要返回值,但有参
accept(T t)
public static void main(String[] args) {
String message = "参数值";
result(x->System.out.println(x), message);
}
public static void result(Consumer<String> fuConsumer, String message) {
fuConsumer.accept(message);
}
Function<T,R>接口 有参有返回值
R apply(T)
public static void result(Function<String, Integer> function,String str){
int num = function.apply(str);
System.out.println(num*10);
}
public static void main(String[] args) {
result(str->Integer.parseInt(str), "123");
}
Predicate 断言接口,就是做一些判断,布尔返回值
boolean test(T) 主要用于校验
public static void call(Predicate<String> predicate,String name) {
// 校验用户名,只能是大小写字母,数字,下划线,并且是6-10位
boolean result = predicate.test(name);
System.out.println(result);
}
public static void main(String[] args) {
call(str->str.matches("\\w{6,10}"), "xx222x");
}
StreamAPI
流式处理,可以对集合进行数据处理
Stream 是Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用SQL 执行的数据库查询。也可以使用Stream API 来并行执行操作。简言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
Stream 和Collection 集合的区别:
Collection 是一种静态的内存数据结构,而Stream 是有关计算的。前者是主要面向内存,存储在内存中,后者主要是面向CPU,通过CPU 实现计算。
Stream到底是什么
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,Stream讲的是计算!”
注意:
①Stream 自己不会存储元素。
②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
// 1 通过数组 创建 Stream.of
String[] strings = {"q","w","e","r","d","f"};
Stream<String> stream1 = Stream.of(strings);
// 2 通过集合
List<String> strings2 = Arrays.asList(strings);
Stream<String> stream2 = strings2.stream();
// 3 通过Stream.generate : 是一个无限流(无限大),建议再操作的时候,通过limit进行最大数量控制
// 参数 Supplier,有一个get方法,是无参有返回值
// get方法的返回值 就作为集合中的数据,下面这中就等于都赋值为1
Stream<Integer> generate = Stream.generate(()->1);
// 可以使用limit 设置元素个数
generate.limit(3).forEach(x->System.out.println(x));
// 4 通过Stream.Iterate 来创建
// 是一个有序的无限流,所以建议使用limit进行最大数量控制
// 第一个参数是数据的起始值,第二个参数是Function,所以是有参有返回值
// 1 就是起始值是1 , x+2 步长为2 , 就类似于一个死循环,起始值是1,步长为2
Stream<Integer> iterate = Stream.iterate(1, x->x+2);
iterate.limit(3).forEach(x->System.out.println(x));
// 5 通过已有类API
String str = "abc";
IntStream chars = str.chars();
chars.forEach(x->System.out.println(x));
中间操作-转换算子
常用的转换算子 : filter,distinct,map,limit,skip,flatMap
filter : 对元素进行过滤筛选,不符合条件的就不要了
distinct : 去除重复
skip : 跳过多少个元素
imit : 取一个集合中的 前几个数据
map : 在集合的遍历中对数据进行操作,比如更改,公司所有员工薪水涨10%
flatMap : 解决字符串数组
注意 : 如果只使用转换算子,不会真正进行计算
List<String> strings = Arrays.asList("a", "b", "c", "a");
Stream<String> stream = strings.stream();
/**
* filter : 对元素进行过滤,不符合条件的就不要了,返回true 就要,返回false 就不要
*
* collect : 把符合条件的转换为集合,属于动作算子(终止操作),
*/
List<String> value = stream.filter(x -> x.equals("a")).collect(
Collectors.toList());
System.out.println(value);
// 重新生成流
stream = strings.stream();
// 跳过一个 就是bca
value = stream.skip(1).collect(Collectors.toList());
System.out.println(value);
// 重新生成流
stream = strings.stream();
/**
* map : 在集合的遍历中对数据进行操作,比如更改,公司所有员工薪水涨10%
*
* 返回值是什么 集合中数据就是什么
*/
List<Integer> integers = Arrays.asList(1000,999,1200,3320);
Stream<Integer> stream2 = integers.stream();
List<Integer> value2 = stream2.map(x->x+10000).collect(Collectors.toList());
System.out.println(value2);
// 重新生成流
stream = strings.stream();
// 去重
value = stream.distinct().collect(Collectors.toList());
System.out.println(value);
// 重新生成流
stream = strings.stream();
// 取一个集合中的前几条数据
value = stream.skip(1).limit(2).collect(Collectors.toList());
System.out.println(value);
String str = "我我我我,,我我,,要我我要要要要,,学要要要要,,,,编要要要我要我要我要,,,程我要".replaceAll(",", "");
IntStream stream3 =str.chars();
stream3.distinct().forEach(x->System.out.println((char)x));
strings = Arrays.asList("1,2,3","a,b,c");
/**
* 本来是两个字符串 只不过字符串中有 , 逗号分割
*
* 通过操作 就可以把逗号去掉 生成6个数据
*/
stream = strings.stream();
value = stream.map(x->x.split(",")).flatMap(arr -> Arrays.stream(arr)).collect(Collectors.toList());
// [1, 2, 3, a, b, c]
System.out.println(value);
System.out.println(value.size());
动作算子
常用的动作算子
循环 forEach
收集器 collect
计算 min,max,count,average
List<String> strings = Arrays.asList("a","b","c");
Stream<String> stream = strings.stream();
// forEach
stream.filter(x->x.equals("a")).forEach(x->System.out.println(x));
stream = strings.stream();
// 统计个数
long count = stream.filter(x->x.equals("a")).count();
System.out.println(count);
stream = strings.stream();
// 收集器,把结果转换为集合
List<String> value = stream.map(x->x+"--").collect(Collectors.toList());
System.out.println(value);