Java 8 引入了流式操作(Stream API)和 Lambda 表达式,极大地简化了集合操作,并提供了更加直观和简洁的编程风格。这两个新特性相辅相成,使得处理集合数据变得更加高效和易读。
一、Lambda 表达式
1. 概述
Lambda 表达式是一种简洁的方式来实现接口中的单一抽象方法。它让你能够以一种简明的方式定义匿名函数,使代码更为简洁和易读,尤其在处理集合或自定义函数时。
Lambda 表达式的基本结构如下:
(parameters) -> expression
或
(parameters) -> { statements; }
2. 语法
无参数的 Lambda 表达式:
() -> System.out.println("Hello World");
带一个参数的 Lambda 表达式:
(x) -> x * x;
// 或简化为 x -> x * x; 省略括号
带多个参数的 Lambda 表达式:
(a, b) -> a + b;
带有代码块的 Lambda 表达式:
(x, y) -> {
int sum = x + y;
return sum;
};
3. Lambda 表达式的使用场景
Lambda 表达式最常用于需要实现函数式接口的场景。函数式接口是指仅有一个抽象方法的接口,如 Runnable、Comparator 和自定义的接口。例如:
@FunctionalInterface
public interface MyFunction {
int apply(int x, int y);
}
使用 Lambda 表达式实现:
MyFunction add = (x, y) -> x + y;
System.out.println(add.apply(5, 3)); // 输出 8
4. 常用的函数式接口
Java 8 提供了许多内置的函数式接口来支持 Lambda 表达式的使用:
Predicate<T>:接收一个参数,返回一个布尔值,通常用于过滤。
Predicate<Integer> isPositive = x -> x > 0;
Consumer<T>:接收一个参数,不返回结果,常用于遍历和打印。
Consumer<String> printer = s -> System.out.println(s);
Function<T, R>:接收一个参数,返回一个结果,用于转换操作。
Function<Integer, String> intToString = x -> String.valueOf(x);
Supplier<T>:不接收参数,返回一个结果,通常用于延迟执行。
Supplier<Double> random = () -> Math.random();
二、Stream API(流式操作)
Stream 是 Java 8 提供的新 API,用于处理集合数据。Stream 是一种高效处理集合的工具,允许开发者以声明式编程方式对集合进行复杂操作,如过滤、排序、映射等。Stream 可以链式调用多个操作,并且不会修改原集合。
1. Stream 的基本操作类型
Stream 中的操作分为两类:中间操作 和 终端操作。
1.1 中间操作(Intermediate Operations)
中间操作会返回一个新的 Stream,允许后续进行进一步操作。这类操作是惰性的,只有在执行终端操作时才会被触发。
filter:根据条件过滤元素
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.stream().filter(n -> n > 2).forEach(System.out::println);
map:将元素转换为另一种形式
List<String> words = Arrays.asList("java", "lambda", "stream");
words.stream().map(String::toUpperCase).forEach(System.out::println);
sorted:对元素进行排序
numbers.stream().sorted().forEach(System.out::println);
distinct:去除重复元素
List<Integer> nums = Arrays.asList(1, 2, 2, 3, 4);
nums.stream().distinct().forEach(System.out::println); // 输出 1 2 3 4
1.2 终端操作(Terminal Operations)
终端操作会触发 Stream 的处理,生成结果或副作用。终端操作会关闭 Stream,因此 Stream 只能使用一次。
forEach:对每个元素执行操作
numbers.stream().forEach(System.out::println);
collect:将流转换成集合、数组或其他数据结构
List<String> filteredWords = words.stream().filter(w -> w.length() > 4).collect(Collectors.toList());
count:计算流中元素的数量
long count = numbers.stream().filter(n -> n > 2).count();
reduce:对元素进行归约操作,如求和、求乘积等
int sum = numbers.stream().reduce(0, (a, b) -> a + b);
2. 示例:使用 Stream 处理集合
2.1 过滤与映射
List<String> names = Arrays.asList("John", "Alice", "Bob", "Charlie");
List<String> result = names.stream()
.filter(name -> name.length() > 3) // 过滤长度大于 3 的字符串
.map(String::toUpperCase) // 转换为大写
.collect(Collectors.toList()); // 收集到 List
System.out.println(result); // 输出 [JOHN, ALICE, CHARLIE]
2.2 链式操作
Stream API 支持链式调用多个操作:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
int sum = numbers.stream()
.filter(n -> n % 2 == 0) // 过滤偶数
.map(n -> n * n) // 将偶数平方
.reduce(0, Integer::sum); // 求和
System.out.println(sum); // 输出 56 (2^2 + 4^2 + 6^2)
2.3 并行流
Java 8 提供了 parallelStream() 来并行处理数据,提高性能。使用并行流可以自动地并行执行 Stream 操作。
List<Integer> largeNumbers = Arrays.asList(100, 200, 300, 400, 500);
largeNumbers.parallelStream().forEach(System.out::println); // 并行输出
3. Stream 的常用操作方法
- filter(Predicate):根据条件过滤元素。
- map(Function):将元素映射为另一种类型。
- flatMap(Function):将多个流合并为一个流。
- distinct():去除流中的重复元素。
- sorted():对流中的元素进行排序。
- limit(long n):限制输出前 n 个元素。
- skip(long n):跳过前 n 个元素。
4. Collectors
Collectors 是一个实用工具类,提供了许多静态方法,用于将流的结果收集到各种不同的数据结构中,如 List、Set、Map 等。
toList():将结果收集到 List 中。
List<Integer> list = numbers.stream().collect(Collectors.toList());
toSet():将结果收集到 Set 中,去除重复。
Set<String> set = names.stream().collect(Collectors.toSet())
joining():将字符串流拼接为一个字符串。
String result = names.stream().collect(Collectors.joining(", "));
System.out.println(result); // 输出:John, Alice, Bob, Charlie
总结
Java 8 的 Lambda 表达式和 Stream API 大大提高了代码的简洁性和可读性。Lambda 表达式简化了函数式接口的使用,而 Stream 提供了一种声明式的方式来处理集合。结合这两者,可以更高效地进行数据操作并简化代码逻辑,减少了大量的样板代码。