引言
随着Java 8的发布,Lambda表达式成为了Java编程语言中一个引人注目的新特性。Lambda表达式不仅简化了代码,还使得Java更加贴近于函数式编程的范式。本文将深入探讨Lambda表达式的概念、语法、应用场景以及它如何改变我们的编程方式。
什么是Lambda表达式?
Lambda表达式是一种匿名函数,它允许你以更简洁的方式表示只有一个抽象方法(即函数式接口)的接口的实例。简单来说,Lambda表达式就是一段可以传递的代码,它定义了一个操作的实现,但没有名称。
函数式接口
在Java中,Lambda表达式只能与函数式接口一起使用。函数式接口是只包含一个抽象方法的接口(可以有多个默认方法或静态方法)。Java 8引入了@FunctionalInterface注解,用于指示一个接口是函数式接口,但这不是强制性的,只要接口满足函数式接口的定义即可。
Lambda表达式的语法
Lambda表达式的基本语法如下:
(parameters) -> expression
或者对于包含多条语句的情况:
(parameters) -> { statements; }
其中,parameters是Lambda表达式接收的参数列表,->是Lambda操作符,expression或{ statements; }是Lambda体的实现。
示例:
//Java中用于函数式编程的几个关键接口
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
//BiFunction<T, U, R> - 接受两种类型的参数,并返回一个结果。
import java.util.List;
import java.util.function.BiFunction;
//BinaryOperator<T> - 是BiFunction的特化版本,它接受两个相同类型的参数,并返回相同类型的结果。
import java.util.function.BinaryOperator;
//Consumer<T> - 接受一个参数,执行操作但不返回任何结果(通常用于副作用,如打印)。
import java.util.function.Consumer;
public class Lamda {
public static void main(String[] args) {
// 用匿名内部类写
Runnable printHello = new Runnable() {
@Override
public void run() {
System.out.println("Hello Lambda1!");
}
};
printHello.run();
// 方式一:直接调用一个无参的Lambda表达式
Runnable printHello1 = () -> System.out.println("1:Hello Lambda1!");
printHello1.run();
// 方式二:定义一个有参的Lambda表达式,并将其赋值给一个变量
Consumer<String> printMessage = (String message) -> System.out.println(message);
// 使用定义好的Lambda表达式打印消息
String message = "2:Hello Lambda!";
printMessage.accept(message);
// 方式三:只有一个参数,省略小括号的Lambda表达式
Consumer<String> printName = name -> System.out.println(name);
printName.accept("3:Hello Lambda!");
// 方式四:有两个以上参数,有返回值的Lambda表达式
// 使用Integer类型的参数来匹配BinaryOperator<Integer>的期望
BinaryOperator<Integer> add = (Integer a, Integer b) -> a + b;
System.out.println("Sum: " + add.apply(5, 10)); //
// 方式五:Lambda体中只有一条语句,省略return和大括号的Lambda表达式
BiFunction<String, String, Integer> strBi = (String s1, String s2) -> s1.length() - s2.length();
System.out.println("Length Difference: " + strBi.apply("Hello", "World"));
// 语法格式六 : Lambda 表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即“类型推断”
List<String> fruits = Arrays.asList("Banana","Apple", "Cherry");
Collections.sort(fruits, (String a, String b) -> a.compareTo(b));
System.out.println(fruits);
fruits.sort((a, b) -> b.compareTo(a));
// 打印排序后的列表
System.out.println(fruits);
}
}
Lambda表达式应用
Lambda表达式在Java中有着广泛的应用,特别是在与集合(Collections)和流(Streams)API结合使用时。以下是一些Lambda表达式常见的应用场景:
集合操作:使用forEach、map、filter等方法对集合进行遍历、转换和过滤。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.List;
import java.util.Arrays;
import java.util.function.Consumer;
public class LamdaTwo {
public static void main(String[] args) {
// 用forEach()方法 遍历集合
List<String> nameList = Arrays.asList("Alice", "Bob", "Charlie","David");
// 要使用匿名内部类实现Consumer接口
nameList.forEach(new Consumer<String>() {
@Override
public void accept(String name) {
System.out.println(name);
}
});
// 使用Lambda表达式可以更简洁地实现相同的功能:
nameList.forEach(name-> System.out.println(name));
// 过滤集合 使用 Stream API:通过调用 names.stream() 将列表转换为 Stream。Stream API 允许您以声明性方式处理集合。
List<String> names = Arrays.asList("Bob","Alice", "David", "Charlie");
// names.stream() 将 names 列表转换成 Stream。
// Stream:一个元素序列,这些元素可以是独立来源的,比如数组或集合,也可以是操作产生的
List<String> filteredNames = names.stream().filter(name -> name.startsWith("C"))
.collect(Collectors.toList());
System.out.println(filteredNames);
// 3. 映射集合
// 应用映射操作:通过 map 操作对 Stream 中的每个元素应用一个函数
List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
List<Integer> squaredNumbers = numbers.stream()
.map(number -> number * number)
.collect(Collectors.toList());
System.out.println(squaredNumbers);
}
}
线程:通过Runnable和Callable接口,Lambda表达式可以简洁地表示线程任务。
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* @Version:1.0
* @Description: TODO(一句话描述该类的功能)
* @Date: 2024/8/14 13:15
* @Author: tao
*/
public class LamdaTHread {
public void firth1() {
// 匿名内部类:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello from a thread!");
}
}).start();
new Thread(() -> {
System.out.println("Hello from a thread!");
}).start();
}
public void secondth2() {
// 创建一个 Runnable 任务
Runnable task = () -> System.out.println("Task is running.");
// 创建线程并启动
new Thread(task).start();
}
public void thirdth3() {
// 这是一个Lambda表达式,定义了异步执行的任务,supplyAsync方法 :返回一个CompletableFuture对象,代表异步操作的结果
CompletableFuture.supplyAsync(() -> {
// 任务的结果是字符串 "Result of a computation"
return "Result of a computation";
}).thenApply(result -> {
// 将返回的结果转大写
return result.toUpperCase();
}).thenAccept(result -> {
// thenAccept方法正在使用 thenApply 方法的结果进行操作
// 这里使用方法引用System.out::println,将结果打印到控制台
System.out.println(result);
});
}
public static void main(String[] args) {
LamdaTHread lambdaThread = new LamdaTHread();
// 调用 firth1 方法,它将创建并启动一个新线程
lambdaThread.firth1();
// 调用 secondth2 方法,它将创建 Runnable 并启动一个新线程
lambdaThread.secondth2();
// 调用 thirdth3 方法,它将使用 CompletableFuture 异步执行任务
lambdaThread.thirdth3();
}
}
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 线程池示例类,演示如何使用Lambda表达式和线程池执行多个任务。
*/
public class LambdaThreadPool {
private static final int POOL_SIZE = 10;/// 线程池的大小
// 需要执行的任务数量
private static final int TASK_COUNT = 20;// // 需要执行的任务数量
private static AtomicInteger completedTaskCount = new AtomicInteger(0);//使用AtomicInteger线程安全地跟踪已完成的任务数量
public void fourth4() {
// 创建固定大小的线程池
ExecutorService executorService = Executors.newFixedThreadPool(POOL_SIZE);
// 创建Future数组,用于存储由线程池异步执行的任务的结果
Future<?>[] futures = new Future<?>[TASK_COUNT];
for (int i = 0; i < TASK_COUNT; i++) {
final int taskId = i;
// 提交任务到线程池,并存储返回的Future对象
futures[i] = executorService.submit(() -> {
try {
// 打印任务正在运行的信息
System.out.println("Task " + taskId + " is running in a thread pool.");
// 模拟任务执行时间
Thread.sleep(1000);
// 任务完成,增加完成计数
completedTaskCount.incrementAndGet();
} catch (InterruptedException e) {
// 重新设置中断状态,并打印中断信息
Thread.currentThread().interrupt();
System.out.println("Task " + taskId + " was interrupted.");
}
});
}
// 关闭线程池,不再接受新任务
executorService.shutdown();
try {
// 等待已提交的任务执行完成,设置超时时间
if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
// 如果超时,则尝试立即关闭线程池
executorService.shutdownNow();
}
} catch (InterruptedException e) {
// 如果当前线程被中断,则立即关闭线程池并重新设置中断状态
executorService.shutdownNow();
Thread.currentThread().interrupt();
}
// 检查所有任务是否完成
for (Future<?> future : futures) {
try {
// 等待任务完成,捕获并处理可能抛出的异常
future.get();
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
}
// 打印所有任务完成的信息
System.out.println("All tasks completed. Total tasks: " + TASK_COUNT + ", Completed tasks: " + completedTaskCount);
}
public static void main(String[] args) {
// 创建LambdaThreadPool实例并调用fourth4方法
LambdaThreadPool lambdaThreadPool = new LambdaThreadPool();
lambdaThreadPool.fourth4();
}
}
事件监听器:对于只包含单个抽象方法的监听器接口,可以使用Lambda表达式来简化事件处理代码。
函数式接口的其他实现:如Predicate、Consumer、Function等,这些接口在Java 8的流API中大量使用。
Lambda表达式的优势
代码简洁:Lambda表达式使得代码更加简洁、易读。
易于并行:结合Java 8的流API,Lambda表达式可以方便地进行并行计算,提高程序性能。
易于理解:对于熟悉函数式编程的开发者来说,Lambda表达式更加直观、易于理解。
结论
Lambda表达式是Java 8中一个关键特性,它不仅简化了代码,还使得Java编程更加现代化,更接近函数式编程的范式。它在集合操作、并行计算和事件处理等方面提供了强大的支持,是现代Java开发中不可或缺的一部分。
Lambda表达式的引入不仅简化了代码,还使得Java更加贴近于函数式编程的范式。在实际编程中,Lambda表达式可以应用于很多场景,例如在集合框架中实现排序、过滤和映射等操作。此外,Lambda表达式还可以用于函数式编程,特别是在Java Streams API中。
通过深入探讨Lambda表达式的概念、语法、应用场景以及它如何改变我们的编程方式,我们可以更好地理解和应用这一特性,从而提高代码的简洁性和可读性。