Java 8引入的 Lambda表达式 和 Stream API 是函数式编程范式的核心特性,彻底改变了Java代码的编写方式。它们简化了集合操作、提升了代码可读性,并通过并行处理优化了性能。本文将从基础语法到实战应用,系统解析Lambda与Stream的核心概念,并结合实际案例展示其强大能力。
Lambda表达式本质上是一个匿名函数,用于简化函数式接口(仅含一个抽象方法的接口)的实现。它的核心目标是让代码更简洁、更聚焦于业务逻辑。
(参数列表) -> { 代码块 }
return
关键字。() -> ...
。示例对比传统写法与Lambda:
// 传统匿名内部类
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("Hello");
}
};
// Lambda简化
Runnable r2 = () -> System.out.println("Hello");
Lambda表达式必须与函数式接口匹配。Java内置了多种函数式接口:
Consumer
:接收一个参数,无返回值。void accept(T t)
Supplier
:无参数,返回一个值。T get()
Function
:接收T类型参数,返回R类型。R apply(T t)
Predicate
:接收T类型参数,返回布尔值。boolean test(T t)
示例:使用Predicate过滤集合
List<String> list = Arrays.asList("Java", "Python", "C++");
Predicate<String> filter = s -> s.length() > 3;
list.stream().filter(filter).forEach(System.out::println); // 输出Java、Python
进一步简化Lambda表达式的语法:
ClassName::staticMethod
instance::method
ClassName::new
示例:
// 传统Lambda写法
list.forEach(s -> System.out.println(s));
// 方法引用简化
list.forEach(System.out::println);
Stream是数据渠道,用于操作集合、数组等数据源,支持顺序和并行处理。Stream操作分为两类:
filter
, map
, sorted
)。forEach
, collect
, reduce
)。list.stream()
Arrays.stream(array)
Stream.of("a", "b", "c")
Stream.iterate(0, n -> n + 2)
操作 | 说明 | 示例 |
---|---|---|
filter |
过滤元素 | stream.filter(s -> s.length() > 3) |
map |
转换元素(一对一) | stream.map(String::toUpperCase) |
flatMap |
扁平化转换(一对多) | stream.flatMap(list -> list.stream()) |
sorted |
排序 | stream.sorted(Comparator.reverseOrder()) |
distinct |
去重 | stream.distinct() |
limit |
截取前N个元素 | stream.limit(5) |
操作 | 说明 | 示例 |
---|---|---|
forEach |
遍历元素 | stream.forEach(System.out::println) |
collect |
将流转换为集合或其他数据结构 | stream.collect(Collectors.toList()) |
reduce |
聚合元素(如求和、求最大值) | stream.reduce(0, Integer::sum) |
count |
统计元素数量 | stream.count() |
anyMatch |
是否存在至少一个元素满足条件 | stream.anyMatch(s -> s.startsWith("A")) |
allMatch |
是否所有元素都满足条件 | stream.allMatch(s -> s.length() > 2) |
List<String> words = Arrays.asList("apple", "banana", "apple", "orange", "banana");
Map<String, Long> frequency = words.stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
// 输出:{apple=2, orange=1, banana=2}
List<Person> people = Arrays.asList(
new Person("Alice", 25),
new Person("Bob", 30),
new Person("Charlie", 20)
);
// 按年龄降序,若年龄相同按姓名升序
List<Person> sorted = people.stream()
.sorted(Comparator.comparingInt(Person::getAge).reversed()
.thenComparing(Person::getName))
.collect(Collectors.toList());
List<Integer> numbers = IntStream.range(1, 1_000_000).boxed().collect(Collectors.toList());
// 顺序流
long start = System.currentTimeMillis();
long sum = numbers.stream().mapToLong(Integer::longValue).sum();
System.out.println("顺序流耗时:" + (System.currentTimeMillis() - start) + "ms");
// 并行流
start = System.currentTimeMillis();
sum = numbers.parallelStream().mapToLong(Integer::longValue).sum();
System.out.println("并行流耗时:" + (System.currentTimeMillis() - start) + "ms");
filter
、map
)。limit
、findFirst
)。某些终端操作(如findFirst
、anyMatch
)不需要处理全部元素即可返回结果:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 10, 20);
// 找到第一个大于5的数后立即停止处理
Optional<Integer> result = numbers.stream()
.filter(n -> n > 5)
.findFirst();
Stream操作应尽量保持无状态,避免修改外部变量:
// 错误写法:在Lambda中修改外部变量
List<String> output = new ArrayList<>();
list.stream().forEach(s -> output.add(s));
// 正确写法:使用collect
List<String> output = list.stream().collect(Collectors.toList());
使用IntStream
、LongStream
、DoubleStream
避免装箱开销:
IntStream.range(1, 100)
.filter(n -> n % 2 == 0)
.average(); // 直接计算原始类型平均值
Lambda与Stream API让Java代码更简洁、更具表现力,但也需注意合理使用:
for
循环,提升可读性。stream.findFirst().orElse(defaultValue)
。示例:链式调用的可读性优化
// 不推荐的写法
List<String> result = list.stream()
.filter(s -> s != null)
.map(String::trim)
.filter(s -> !s.isEmpty())
.map(String::toUpperCase)
.sorted()
.collect(Collectors.toList());
// 推荐的写法(通过换行和注释增强可读性)
List<String> result = list.stream()
.filter(Objects::nonNull) // 过滤null值
.map(String::trim) // 去除首尾空格
.filter(s -> !s.isEmpty()) // 过滤空字符串
.map(String::toUpperCase) // 转为大写
.sorted(Comparator.naturalOrder()) // 自然排序
.collect(Collectors.toList());
通过掌握Lambda与Stream API,开发者可以写出更简洁、高效且易于维护的Java代码,尤其是在处理集合数据和复杂业务逻辑时,其优势更加显著。