Java8的新特性——Stream与completableFuture详解
1.Stream
第一部分:Stream 综述
什么是 Stream: Stream 是 Java 8 引入的用于处理集合的一种抽象工具,它允许我们以更简洁的方式进行数据操作。Stream 本身并不存储数据,而是用于对数据源(如集合、数组等)进行操作的管道。
适用场景: Stream 适用于需要对集合进行复杂数据处理的场景,如过滤、映射、排序、聚合等。它特别适合于处理大量数据时,可以简化代码并提高可读性。
好处:
- 简洁性:通过声明式的 API,可以减少样板代码。
- 可读性:链式调用使得代码逻辑更加清晰。
- 性能优化:通过惰性求值和短路操作,可以避免不必要的计算。
第二部分:好处的示例代码对比
示例 1:命令式 vs 声明式
命令式方式:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
List<String> result = new ArrayList<>();
for (String name : names) {
if (name.length() > 3) {
result.add(name);
}
}
System.out.println(result); // 输出: [Alice, Charlie]
声明式方式:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
List<String> result = names.stream()
.filter(name -> name.length() > 3)
.collect(Collectors.toList());
System.out.println(result); // 输出: [Alice, Charlie]
在这个对比中,声明式方式更加简洁,逻辑一目了然。
示例 2:使用 map 和 collect
命令式方式:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<String> uppercasedNames = new ArrayList<>();
for (String name : names) {
uppercasedNames.add(name.toUpperCase());
}
System.out.println(uppercasedNames); // 输出: [ALICE, BOB, CHARLIE]
声明式方式:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<String> uppercasedNames = names.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(uppercasedNames); // 输出: [ALICE, BOB, CHARLIE]
第三部分:Stream 的并行 API
并行流: Java 8 提供的并行流使得我们可以通过简单的 API 使用多核处理器来提高性能。通过调用 parallelStream()
方法,可以轻松实现并行处理。
示例:使用并行流
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
List<String> filteredNames = names.parallelStream()
.filter(name -> name.startsWith("A"))
.collect(Collectors.toList());
System.out.println(filteredNames); // 输出: [Alice]
Fork/Join 框架: 并行流背后使用了 Fork/Join 框架,它通过将任务分解成多个子任务,利用多核 CPU 的优势来并行处理。具体来说,Fork/Join 框架使用了一个工作窃取算法,当某个线程完成任务时,它可以“窃取”其他线程的任务来处理。
- Fork:将任务分解成更小的子任务。
- Join:合并子任务的结果。
通过这些特性,Java 的并行流能够有效地利用系统资源,提高数据处理的效率。
总结
Stream API 通过简化数据处理过程,提高了代码的可读性和可维护性。而并行 API 则使得在多核处理器上进行高效的数据处理成为可能,进一步提升了性能。
2.completableFuture
第一部分:Future 的介绍
Future 是什么: Future
是 Java 提供的一个接口,用于表示异步计算的结果。它提供了一种机制来在未来的某个时间点获取任务的执行结果。
异步: 异步编程指的是程序在执行某个操作时不需要等待该操作完成,而是可以继续执行其他操作。对于 Future
来说,提交的任务在一个独立的线程中执行,主线程可以继续工作,而不是被阻塞。
阻塞和轮询: 使用 Future
获取结果时,通常需要调用 get()
方法,这个方法会阻塞当前线程,直到任务完成。如果任务尚未完成,调用 get()
会导致线程进入等待状态,直到结果可用。这种阻塞操作可能导致性能问题。
不支持回调: Future
本身不支持回调。当任务完成时,无法直接通知主线程,这会导致回调地狱的问题。如果需要在任务完成后执行某些操作,通常需要在主线程中进行轮询,检查任务是否完成,这种方法繁琐且不优雅。
第二部分:CompletableFuture 的拓展
CompletableFuture 是什么: CompletableFuture
是 Future
的一个扩展,提供了更强大的功能,允许我们以非阻塞的方式处理异步计算结果。
回调方法: CompletableFuture
提供了一系列回调方法,如 thenApply()
、thenAccept()
和 thenRun()
,允许我们在任务完成时定义要执行的操作。这使得编写异步代码变得更加简单和可读。
示例:传统的 Future 和 CompletableFuture 的对比
使用 Future
:
ExecutorService executor = Executors.newFixedThreadPool(1);
Future<Integer> future = executor.submit(() -> {
Thread.sleep(1000); // 模拟长时间计算
return 42;
});
// 阻塞获取结果
try {
Integer result = future.get(); // 这里会阻塞
System.out.println("Result: " + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
使用 CompletableFuture
:
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000); // 模拟长时间计算
} catch (InterruptedException e) {
e.printStackTrace();
}
return 42;
});
// 非阻塞获取结果,并定义回调
completableFuture.thenAccept(result -> System.out.println("Result: " + result));
回调地狱的改善: 传统的回调实现往往导致“回调地狱”,即代码层层嵌套,难以维护。CompletableFuture
通过链式调用的方式改善了这一问题,使得代码更清晰。
示例:使用 CompletableFuture 的链式调用:
CompletableFuture.supplyAsync(() -> {
// 计算过程
return 42;
}).thenApply(result -> {
// 处理结果
return result * 2;
}).thenAccept(finalResult -> {
// 最终处理
System.out.println("Final Result: " + finalResult);
});
改善的原因: CompletableFuture
能够改善回调地狱的原因在于它实现了两个接口:
-
CompletionStage 接口:
- 提供了一系列的组合方法,可以实现非阻塞的回调。
- 支持链式调用,减少嵌套。
-
Future 接口:
- 保留了
Future
的所有特性,可以异步获取结果。
- 保留了
第三部分:总结
CompletableFuture
是 Java 8 引入的重要特性,它在处理异步编程时提供了强大的功能,显著改善了传统 Future
的不足。通过回调方法和链式调用,CompletableFuture
使得异步编程更为简单和可读,有效避免了回调地狱的问题。
在使用 CompletableFuture
时,我们可以轻松定义任务完成后的操作,而无需阻塞当前线程,提升了代码的效率和可维护性。