一、Lambda表达式介绍
1.1 Lambda简介
Lambda表达式是Java 8引入的一种新特性,用于简化代码编写和提高代码可读性。它允许我们以更简洁的方式定义和传递匿名函数。
Lambda表达式由箭头"->"分隔为两部分:左侧为参数列表,右侧为表达式主体。它通常用于替代需实现函数式接口的匿名内部类。
Lambda表达式具有以下特点:
- 简洁性:Lambda表达式可以极大地减少冗余的代码,使得程序更加精简和易读。
- 函数式编程:Lambda表达式鼓励使用函数式编程思想,通过将函数视为第一等公民来处理数据和逻辑。
- 延迟执行:Lambda表达式允许延迟执行代码,比如在需要的时候才进行计算或处理。
Lambda表达式的应用场景包括但不限于:
- 集合操作:可以通过Lambda表达式对集合进行遍历、过滤和转换等操作。
- 多线程编程:可以将Lambda表达式作为线程任务,简化多线程编程的代码量。
- 排序和比较:可以通过Lambda表达式定义自定义的排序规则和比较器。
Lambda表达式的语法形式可以灵活运用,适应不同的需求和场景。它是现代Java编程中的重要工具,使代码更加简洁、灵活和易于维护。
1.2 接口要求
虽然使用 Lambda 表达式可以对某些接口进行简单的实现,但并不是所有的接口都可以使用 Lambda 表达式来实现。Lambda 规定接口中只能有一个需要被实现的方法,不是规定接口中只能有一个方法。
jdk 8 中有另一个新特性:default, 被 default 修饰的方法会有默认实现,不是必须被实现的方法,所以不影响 Lambda 表达式的使用。
@FunctionalInterface注解作用
@FunctionalInterface标记在接口上,“函数式接口”是指仅仅只包含
一个抽象方法的接口。
二、Lambda表达式语法
2.1 语法结构
(parameters) -> expression
或
(parameters) ->{ statements;}
语法形式为 () -> {}:
() 用来描述参数列表,如果有多个参数,参数之间用逗号隔开,如果没有参数,留空即可;
-> 读作(goes to),为 lambda运算符 ,固定写法,代表指向动作;
{} 代码块,具体要做的事情,也就是方法体内容;
2.2 Lambda表达式的重要特征
可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
2.3 Lambda案例
// 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)
三、Lambda表达式入门案例
3.1 定义函数接口
/**
* 无返回值,无参数
*/
@FunctionalInterface
interface NoReturnNoParam{
void method();
}
/**
* 无返回值,有一个参数
*/
@FunctionalInterface
interface NoReturnOneParam{
void method(int a);
}
/**
* 无返回值,有多个参数
*/
@FunctionalInterface
interface NoReturnMultiParam{
void method(int a,int b);
}
/**
* 有返回值,无参数
*/
@FunctionalInterface
interface ReturnNoParam{
int method();
}
/**
* 有返回值,有一个参数
*/
@FunctionalInterface
interface ReturnOneParam{
int method(int a);
}
/**
* 有返回值,有多个参数
*/
@FunctionalInterface
interface ReturnMultiParam{
int method(int a ,int b);
}
3.2 实现函数接口
public static void main(String[] args) {
/**
* 无返回值,无参数
*/
/* NoReturnNoParam noReturnNoParam= ()->{
System.out.println("NoReturnNoParam");
};*/
/**
* 简化版
*/
NoReturnNoParam noReturnNoParam=()-> System.out.println("NoReturnNoParam");
noReturnNoParam.method();
/**
* 无返回值,有一个参数
*/
/* NoReturnOneParam noReturnOneParam = (int a)->{
System.out.println("NoReturnOneParam "+a);
};*/
/**
* 简化版
*/
NoReturnOneParam noReturnOneParam = a -> System.out.println("NoReturnOneParam "+a);
noReturnOneParam.method(10);
/**
* 无返回值,有多个参数
*/
/* NoReturnMultiParam noReturnMultiParam= (int a,int b)->{
System.out.println("NoReturnMultiParam "+a+"\t"+b);
};*/
/**
* 简化版
*/
NoReturnMultiParam noReturnMultiParam=(a,b)->System.out.println("NoReturnMultiParam "+a+"\t"+b);
noReturnMultiParam.method(10,20);
/**
* 有返回值,无参数
*/
/* ReturnNoParam returnNoParam = ()->{
System.out.print("ReturnNoParam ");
return 10;
};*/
/**
* 简化版
*/
ReturnNoParam returnNoParam =()->10;
System.out.println(returnNoParam.method());
/**
* 有返回值,有一个参数
*/
/*ReturnOneParam returnOneParam = (int a)->{
System.out.print("ReturnOneParam ");
return a;
};*/
/**
* 简化版
*/
ReturnOneParam returnOneParam = a -> a;
System.out.println(returnOneParam.method(10));
/**
* 有返回值,有多个参数
*/
/*ReturnMultiParam returnMultiParam = (int a,int b)->{
System.out.print("ReturnMultiParam ");
return a+b;
};*/
/**
* 简化版
*/
ReturnMultiParam returnMultiParam = (a,b) -> a+b;
System.out.println(returnMultiParam.method(10,20));
}
四、Lambda表达式的使用
4.1 Lambda表达式的引用方法
4.1.1 要求
- 参数的个数以及类型需要与函数接口中的抽象方法一致。
- 返回值类型要与函数接口中的抽象方法的返回值类型一致。
4.1.2 语法
方法归属者::方法名 静态方法的归属者为类名,非静态方法归属者为该对象的引用。
4.1.3 案例
/**
* 要求:
* 1,参数的个数以及类型需要与函数接口中的抽象方法一致。
* 2,返回值类型要与函数接口中的抽象方法的返回值类型一致。
* @param a
* @return
*/
public static int doubleNum(int a){
return 2*a;
}
public int addTwo(int a){
return a+2;
}
public class Test2 {
public static void main(String[] args) {
ReturnOneParam returnOneParam = Test::doubleNum;
int value = returnOneParam.method(10);
System.out.println(value);
Test test = new Test();
ReturnOneParam returnOneParam1 = test::addTwo;
int value1 = returnOneParam1.method(10);
System.out.println(value1);
}
}
4.2 Lambda表达式创建线程
public class Test3 {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName()+" 开始");
new Thread(()->{
for(int i=0;i<20;i++){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" "+i);
}
},"Lambda Thread").start();
System.out.println(Thread.currentThread().getName()+" 结束");
}
}
4.3 Lambda 表达式中的闭包问题
4.3.1 什么是闭包
闭包的本质就是代码片断。所以闭包可以理解成一个代码片断的引用。在Java中匿名内部类也是闭包的一种实现方式。
在闭包中访问外部的变量时,外部变量必须是final类型,虚拟机会帮我们加上 final 修饰关键字。
public class Test4 {
public static void main(String[] args) {
final int num = 10;
NoReturnNoParam noReturnNoParam = ()-> System.out.println(num);
noReturnNoParam.method();
}
}
五、常用的函数接口
5.1 Consumer接口的使用
Consumer 接口是JDK为我们提供的一个函数式接口,该接口也被称为消费型接口。
5.1.1 遍历集合
我们可以调用集合的 public void forEach(Consumer<? super E> action) 方法,通过 lambda 表达式的方式遍历集合中的元素。以下是 Consumer 接口的方法以及遍历集合的操作。
public class Test5 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.forEach(System.out::println);
}
//格式化数字的函数式方法
public void f1(Consumer<Double> c, Double money) {
c.accept(money);
}
@Test
public void f1Test() {
f1(c -> {
DecimalFormat df = new DecimalFormat("#,###.###");//格式化数字,不保留0
System.out.println(df.format(c));
}, 123459687978.78966);
System.out.println("-----------------------------------------------");
f1(c -> {
DecimalFormat df = new DecimalFormat("0,000.000");//格式化数字,可以保留0
System.out.println(df.format(c));
}, 123459687978.78966);
}
}
5.2 Predicate接口的使用
Predicate 是 JDK 为我们提供的一个函数式接口,可以简化程序的编写。
5.2.1 删除集合中的元素
我们通过public boolean removeIf(Predicate<? super E> filter)方法来删除集合中的某个元素,
class PredicateImpl implements Predicate{
@Override
public boolean test(Object o) {
return (o.equals("b"));
}
}
public class Test6 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.removeIf(ele->ele.equals("a"));
list.forEach(System.out::println);
}
/**
* 根据指定规则过滤集合中的的信息然后返回一个过滤后的集合
*/
@Test
public void filterListTest(){
List<String> list = new ArrayList<>();
list.add("hello abc");
list.add("welcome abc");
list.add("hello world");
list.add("你好 abc");
list.add("大家好 abc");
list.add("hello mickey");
list.stream().filter(s -> s.contains("hello")).collect(Collectors.toList()).forEach(System.out::println);
}
}
5.3 Comparator接口的使用
Comparator是 JDK 为我们提供的一个函数式接口,该接口为比较器接口。
5.3.1 元素排序
class ComparatorImpl implements Comparator<String>{
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
}
public class Test7 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("b");
list.add("c");
list.add("d");
list.add("a");
list.sort((o1,o2)->o1.compareTo(o2));
list.forEach(System.out::println);
System.out.println("-----------------");
//引用类方法
list.sort(String::compareTo);
list.forEach(System.out::println);
System.out.println("-----------------");
//使用Comparator接口中的naturalOrder方法做升序排序。
list.sort(Comparator.naturalOrder());
list.forEach(System.out::println);
System.out.println("-----------------");
//使用Comparator接口中的naturalOrder方法做升序排序。
list.sort(Comparator.reverseOrder());
list.forEach(System.out::println);
System.out.println("-----------------");
}
}
六、Stream流介绍
6.1 Stream流简介
Stream是数据渠道,用于操作数据源所生成的元素序列,它可以实现对集合的复杂操作,例如过滤、排序和映射等。Stream不会改变源对象,而是返回一个新的结果集。
6.2 Stream流的生成方式
- 生成流:通过数据源(集合、数组等)创建一个流。
- 中间操作:一个流后面可以跟随零个或者多个中间操作,其目的主要是打开流,做出某种程度的数据过滤/映射,然后返回一个新的流,交给下一个操作使用。
- 终结操作:一旦执行终止操作,就执行中间的链式操作,并产生结果。
6.3 Stream流的常见方法
6.3.1 数据过滤
public class Test8 {
public static void main(String[] args) {
/**
* 1.创建Stream对象
* 2.操作Stream对象
* 3.结束Stream对象
*/
List<String> list = new ArrayList<>();
list.add("oldlu");
list.add("oldlin");
list.add("kevin");
list.add("peter");
//多条件的and关系
list.stream().filter(ele->ele.startsWith("o")).filter(ele->ele.endsWith("n")).collect(Collectors.toList()).forEach(System.out::println);
System.out.println("------------------");
//多条件的or关系
Predicate<String> predicate1 = ele->ele.startsWith("o");
Predicate<String> predicate2 = ele-> ele.endsWith("n");
list.stream().filter(predicate1.or(predicate2)).collect(Collectors.toList()).forEach(System.out::println);
}
//函数式接口过滤求和
@Test
public void f1(){
List<User> list = new ArrayList<>();
list.add(new User(1, "mickey", 18));
list.add(new User(2, "john", 18));
list.add(new User(3, "doc", 18));
list.add(new User(4, "cat", 18));
list.add(new User(5, "tom", 18));
list.add(new User(6, "tomcat", 18));
list.add(new User(7, "mysql", 18));
list.add(new User(8, "java", 18));
System.out.println(list.stream().map(user -> user.getAge()).reduce((d1, d2) -> d1 + d2).get());
}
@Test
public void f2(){
List<User> list = new ArrayList<>();
list.add(new User(1, "amickey", 22));
list.add(new User(2, "johna", 33));
list.add(new User(3, "doca", 19));
list.add(new User(4, "cat", 20));
list.add(new User(5, "toma", 20));
list.add(new User(6, "tomcat", 70));
list.add(new User(7, "mysqla", 73));
list.add(new User(8, "java", 84));
//查看user集合中的名字中是否都包含 a 包含返回true 反之false
System.out.println(list.stream().allMatch(user -> user.getMickey().contains("a")));
}
@Test
public void test3() {
//以()代表流,[]为数组,""为字符串
List<String> list = Arrays.asList("a-b-c", "d-e-f");
//map中的第一个t是"a-b-c",返回的是[a,b,c]一个数组
//map以后流中的元素为([a,b,c],[e,d,f])两个元素,每个元素是数组
//map: ("a-b-c", "d-e-f")---->([a,b,c],[e,d,f]) ,size=2
list.stream().map(t -> t.split("-")).forEach(e -> System.out.println(Arrays.toString(e)));
System.out.println("-----------");
//flatMap第一个t是"a-b-c",返回的是(a,b,c)的一个子流,最后聚合所有子流合并为一个流
//flatMap: ("a-b-c", "d-e-f")---->((a,b,c),(d,e,f))-->(a,b,c,d,e,f) ,size=6
list.stream().flatMap(t -> Stream.of(t.split("-"))).forEach(System.out::println);
}
}
6.3.2 数量限制
public class Test9 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("oldlu");
list.add("oldlin");
list.add("kevin");
list.add("peter");
list.stream().limit(2).collect(Collectors.toList()).forEach(System.out::println);
}
}
6.3.3 元素排序
public class Test10 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("b");
list.add("c");
list.add("a");
list.add("d");
//升序排序
list.stream().sorted().forEach(System.out::println);
System.out.println("----------------------");
list.stream().sorted(Comparator.naturalOrder()).forEach(System.out::println);
System.out.println("----------------------");
//降序排序
list.stream().sorted(Comparator.reverseOrder()).forEach(System.out::println);
}
@Test
public void f1(){
List<User> list = new ArrayList<>();
list.add(new User(1, "mickey", 22));
list.add(new User(2, "john", 33));
list.add(new User(3, "doc", 19));
list.add(new User(4, "cat", 20));
list.add(new User(5, "tom", 20));
list.add(new User(6, "tomcat", 70));
list.add(new User(7, "mysql", 73));
list.add(new User(8, "java", 84));
list.stream().sorted((o1,o2)->Integer.compare(o1.getAge(),o2.getAge())).collect(Collectors.toList()).forEach(System.out::println);
}
}