📖目录
1. 前言
在现代Java编程中,Lambda表达式已经成为一种不可或缺的工具。它不仅简化了代码结构,还为函数式编程提供了强大的支持。
本文全面介绍Java 8中的Lambda表达式,重点解析其核心优势:简化代码、提升可读性和支持函数式编程。通过7大类典型应用场景展示Lambda的实际用法:
- 数据过滤 - 使用filter()实现条件筛选
- 分组统计 - 通过groupingBy()进行数据分类
- 聚合计算 - 利用mapToInt().average()等实现数值统计
- 集合排序 - 采用Comparator.comparingInt()定义排序规则
- 去重合并 - 通过distinct()快速消除重复项
- 并行处理 - 使用parallelStream()提升大数据处理效率
- 自定义收集器 - 组合Collectors实现复杂数据归约
文中包含可直接复用的代码示例,涵盖用户筛选、年龄统计、性别分组等典型场景,帮助开发者快速掌握Lambda在集合操作中的高效应用。
一、什么是Lambda表达式?
Lambda表达式是Java 8引入的一种新特性,用于实现函数式接口(只有一个抽象方法的接口)。它的核心优势在于:
- 简化代码:减少冗长的匿名类写法。
- 提高可读性:使代码更加直观和易懂。
- 增强功能性:支持流式操作(Stream API),便于处理集合数据。
Lambda表达式的语法如下:
(parameters) -> expression
二、Lambda表达式的分类与使用场景
我们将从以下几个方面对Lambda表达式的使用进行分类讲解,并结合代码示例展示其实际应用。
1. 过滤与筛选
在处理集合数据时,经常需要根据条件对数据进行过滤。例如,筛选出年龄大于12岁的用户:
@Test
public void testFilterUsersByAge() {
List<JavaLambdaUser> filteredUsers = userList.stream()
.filter(user -> user.getAge() > 12) // 使用Lambda表达式过滤
.collect(toList());
System.out.println(filteredUsers);
}
说明:
filter
方法接收一个Predicate
(断言)类型的Lambda表达式,用于定义筛选条件。- 这段代码展示了如何利用Lambda表达式简化集合筛选操作。
2. 分组与统计
Lambda表达式在分组和统计操作中也非常有用。例如,按性别分组用户:
@Test
public void testGroupUsersByGender() {
Map<Integer, List<JavaLambdaUser>> groupedUsers = userList.stream()
.collect(Collectors.groupingBy(JavaLambdaUser::getGender)); // 按性别分组
System.out.println(groupedUsers);
}
说明:
Collectors.groupingBy
方法结合Lambda表达式,可以轻松实现分组操作。- 这种方式比传统的循环遍历更加优雅和高效。
3. 聚合操作(Reduce)
Lambda表达式常用于聚合操作,如计算总和或平均值。以下是计算所有用户的平均年龄的示例
@Test
public void testReduceAvg() {
double avgAge = userList.stream()
.mapToInt(JavaLambdaUser::getAge)
.average() // 计算平均值
.orElse(0.0); // 如果为空,返回默认值
System.out.println("Average Age: " + avgAge);
}
说明:
mapToInt
将对象流转换为整数流。average
方法直接返回平均值,无需手动计算。
4. 排序
排序是另一个常见的操作。以下代码展示了如何按年龄对用户列表进行排序:
@Test
public void testSortedList() {
List<JavaLambdaUser> sortedUsers = userList.stream()
.sorted(Comparator.comparingInt(JavaLambdaUser::getAge)) // 按年龄排序
.collect(toList());
System.out.println(sortedUsers);
}
说明:
Comparator.comparingInt
结合Lambda表达式,可以快速定义排序规则。- 排序后的结果可以直接用于后续处理。
5. 去重与合并
在处理集合时,去重和合并是非常常见的需求。以下是去除重复元素的示例:
@Test
public void testDistinct() {
List<String> distinctElements = Arrays.asList("apple", "banana", "apple", "orange").stream()
.distinct() // 去重
.collect(toList());
System.out.println(distinctElements);
}
说明:
distinct
方法基于元素的equals
方法去重。- 结合Stream API,可以轻松实现复杂的数据处理逻辑。
6. 并行流处理
对于大数据量的处理,并行流可以显著提升性能。以下代码展示了如何使用并行流处理集合:
@Test
public void testParallelStream() {
long count = userList.parallelStream()
.filter(user -> user.getAge() > 18) // 筛选成年人
.count(); // 统计数量
System.out.println("Adult Users Count: " + count);
}
说明:
parallelStream
方法会自动将任务分配到多个线程执行。- 对于密集型计算,这种方式可以大幅缩短处理时间。
7. 自定义Collector
有时候,标准的收集器无法满足需求,我们可以自定义 Collector
。以下是一个简单的自定义收集器示例:
@Test
public void testCustomCollector() {
Collector<JavaLambdaUser, ?, Map<Integer, List<String>>> collector = Collectors.groupingBy(
JavaLambdaUser::getGender,
Collectors.mapping(JavaLambdaUser::getName, toList())
);
Map<Integer, List<String>> result = userList.stream().collect(collector);
System.out.println(result);
}
说明:
- 自定义
Collector
可以通过组合不同的收集器来实现复杂的逻辑。 - 这种方式灵活性极高,适用于各种特殊场景。
三、更多实用示例
以下的代码包含更多的示例,以供参考,也可直接拷贝应用在项目中
import lombok.Getter;
import lombok.Setter;
import org.junit.Test;
import org.junit.jupiter.api.Assertions;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.lang.Character.isDigit;
import static java.util.stream.Collectors.toList;
import static org.junit.Assert.*;
// 收集常见的Java函数式编程示例,用于更简洁的编码,特别是在混合包、指数、融资等统计模块
public class JavaLambdaTest {
private static List<JavaLambdaUser> userList = Arrays.asList(
new JavaLambdaUser(2, "李四", 21, 1),
new JavaLambdaUser(1, "张三", 12, 0),
new JavaLambdaUser(4, "赵六", 32, 1),
new JavaLambdaUser(3,"王五", 32, 0)
);
private static List<TradeGroup> tradeGroupList = createTradeGroupList();
public static List<TradeGroup> createTradeGroupList() {
// 创建 TradeGroup 对象并设置 tradeRateLimit
TradeGroup tradeGroupOne = new TradeGroup();
tradeGroupOne.setTradeRateLimit(0.1f);
TradeGroup tradeGroupTwo = new TradeGroup();
tradeGroupTwo.setTradeRateLimit(0.2f);
TradeGroup tradeGroupThree = new TradeGroup();
tradeGroupThree.setTradeRateLimit(0.3f);
// 返回包含所有 TradeGroup 对象的列表
return Arrays.asList(tradeGroupOne, tradeGroupTwo, tradeGroupThree);
}
@Test
public void testList() {
List<String> collected1 = Stream.of("a", "b", "hello").map(stringArg -> stringArg.toUpperCase()).collect(toList());
assertEquals(3, collected1.size());
// 1.过滤
List<String> beginningWithNumbers =
Stream.of("a", "1abc", "abc1") // 定义数据
.filter(value -> isDigit(value.charAt(0))) // 过滤
.collect(toList()); // 转换为List
assertEquals(Arrays.asList("1abc"), beginningWithNumbers);
}
@Test
public void testStreamMapping() {
// 测试字符串转换为大写
List<String> collected1 = Stream.of("a", "b", "hello")
.map(String::toUpperCase)
.collect(Collectors.toList());
assertEquals(Arrays.asList("A", "B", "HELLO"), collected1);
}
@Test
public void testMaxValueInList() {
// 测试列表中的最大值
List<Integer> list = Arrays.asList(3, 5, 2, 9, 1);
int maxInt = list.stream().max(Integer::compareTo).get();
assertEquals(9, maxInt);
}
@Test
public void testMinValueInList() {
// 测试列表中的最小值
List<Integer> list = Arrays.asList(3, 5, 2, 9, 1);
int minInt = list.stream().min(Integer::compareTo).get();
assertEquals(1, minInt);
}
// 测试 TradeGroup 列表中最大 tradeRateLimit
@Test
public void testMaxTradeRateLimit() {
Optional<TradeGroup> maxTradeGroup = tradeGroupList.stream()
.max(Comparator.comparing(TradeGroup::getTradeRateLimit));
assertTrue(maxTradeGroup.isPresent());
// 使用 delta 值来允许一定的误差范围
double delta = 0.0001; // 允许的误差范围
Assertions.assertEquals(delta, 0.3f, maxTradeGroup.get().getTradeRateLimit());
}
// 测试 TradeGroup 列表中最小 tradeRateLimit
@Test
public void testMinTradeRateLimit() {
Optional<TradeGroup> tradeGroupMax = tradeGroupList.stream().max(Comparator.comparing(TradeGroup::getTradeRateLimit));
Optional<TradeGroup> tradeGroupMin = tradeGroupList.stream().min(Comparator.comparing(TradeGroup::getTradeRateLimit));
tradeGroupMax.ifPresent(group -> System.out.println("Max tradeRateLimit: " + group.getTradeRateLimit()));
tradeGroupMin.ifPresent(group -> System.out.println("Min tradeRateLimit: " + group.getTradeRateLimit()));
double delta = 0.0001; // 允许的误差范围
Assertions.assertEquals(delta, 0.1f, tradeGroupMin.get().getTradeRateLimit());
Assertions.assertEquals(delta, 0.3f, tradeGroupMax.get().getTradeRateLimit());
//assertEquals(0.1f, tradeGroupMin.get().getTradeRateLimit());
}
@Test
public void testSortedList() {
// 测试列表排序
List<Integer> list1 = Arrays.asList(3, 5, 1, 10, 8);
List<Integer> sortedList = list1.stream()
.sorted(Integer::compareTo)
.collect(Collectors.toList());
assertEquals(Arrays.asList(1, 3, 5, 8, 10), sortedList);
}
// 测试过滤年龄大于 12 的用户
@Test
public void testFilterUsersByAge() {
List<JavaLambdaUser> filteredUsers = userList.parallelStream()
.filter(user -> user.getAge() > 12)
.collect(Collectors.toList());
assertEquals(3, filteredUsers.size());
}
// 测试按性别分组
@Test
public void testGroupUsersByGender() {
Map<Integer, List<JavaLambdaUser>> groupedByGender = userList.stream()
.collect(Collectors.groupingBy(JavaLambdaUser::getGender));
assertEquals(2, groupedByGender.size());
assertEquals(2, groupedByGender.get(0).size());
assertEquals(2, groupedByGender.get(1).size());
// 另一种写法
Map<Integer, List<JavaLambdaUser>> groupedByGenderV2 = userList.stream()
.collect(Collectors.groupingBy(k -> k.getGender()));
assertEquals(2, groupedByGenderV2.size());
assertEquals(2, groupedByGenderV2.get(1).size());
assertEquals(2, groupedByGenderV2.get(0).size());
Map<Integer, Integer> groupedByGender2 = userList.parallelStream().collect(
Collectors.groupingBy(JavaLambdaUser::getGender, // 按性别
Collectors.summingInt(p -> 1))); // 自增1
assertEquals(2, (int) groupedByGender2.get(1));
assertEquals(2, (int) groupedByGender2.get(0));
Map<Integer, List<String>> map2 = userList.stream()
.collect(
Collectors.groupingBy(
JavaLambdaUser::getGender, // 分组:按性别
Collectors.mapping(JavaLambdaUser::getName,
Collectors.toList()))); // 组装列表:用户名称
Map<Integer, Set<String>> result =
userList.stream().collect(
Collectors.groupingBy(JavaLambdaUser::getGender, // 分组:按性别
Collectors.mapping(JavaLambdaUser::getName,
Collectors.toSet()) // 组装集合:用户名称
)
);
// 验证 map2
verifyMap2(map2);
// 验证 result
verifyResult(result);
}
// Reduce操作:每处理一个元素总是创建一个新值,适用于返回单个结果值的情况
// 获取所有用户的平均年龄
@Test
public void testReduceAvg() {
// mapToInt的pipeline,后面可以是:average,max,min,count,sum
double avg = userList.parallelStream().mapToInt(JavaLambdaUser::getAge)
.average().getAsDouble();
// 验证结果是否等于预期值
assertEquals(24.25, avg, 0.001);
// 添加 assertTrue 语句
assertTrue("Average should be greater than 10", avg > 10);
assertTrue("Average should be less than 20", avg < 98);
}
// 获取所有用户的年龄总和
@Test
public void testReduceSum() {
double sum = userList.parallelStream().mapToInt(JavaLambdaUser::getAge)
.reduce(0, (x, y) -> x + y); // 可以简写为.sum()
// 验证结果是否等于预期值
assertEquals(97.0, sum, 0.001);
// 添加 assertTrue 语句
assertTrue("Sum should be greater than 40", sum > 40);
assertFalse("Sum should be less than 50", sum < 50);
}
private void verifyMap2(Map<Integer, List<String>> map2) {
// 定义预期值
Map<Integer, List<String>> expectedMap2 = new HashMap<>();
expectedMap2.put(1, Arrays.asList("李四", "赵六"));
expectedMap2.put(0, Arrays.asList("张三", "王五"));
// 使用 assertEquals 验证
assertEquals(expectedMap2.size(), map2.size());
for (Integer key : expectedMap2.keySet()) {
assertTrue(map2.containsKey(key));
assertTrue(areListsEqualIgnoreOrder(expectedMap2.get(key), map2.get(key)));
}
}
@Test
public void testReducing() {
// 11.求和:按性别
Map<Integer, Integer> map3 = userList.stream().collect(
Collectors.groupingBy(JavaLambdaUser::getGender, // 分组:按性别
Collectors.reducing(0, JavaLambdaUser::getAge, Integer::sum))); // 求和:年龄
Map<Integer, Integer> map1 = userList.parallelStream().collect(
Collectors.groupingBy(JavaLambdaUser::getGender, // 分组:按性别
Collectors.summingInt(p -> p.getAge()))); // 求和:年龄
Map<Integer, Long> map14 = userList.parallelStream().collect(
Collectors.groupingBy(JavaLambdaUser::getGender, // 分组:性别
Collectors.summingLong(p -> p.getAge()))); // 求和:年龄(并添加类型转换:int -> long)
}
@Test
public void testAveraging() {
// 12.平均值:按性别求年龄的平均值
Map<Integer, Double> map4 = userList.stream().collect(
Collectors.groupingBy(JavaLambdaUser::getGender, // 分组:按性别
Collectors.averagingInt(JavaLambdaUser::getAge))); // 求平均值:年龄
System.out.println("map4 is: " + map4);
}
@Test
public void testMap() {
// 13.快速的做类型转换
List<String> nameList = userList.stream().map(JavaLambdaUser::getName).collect(Collectors.toList());
Set<String> nameSet = userList.stream().collect(
Collectors.mapping(JavaLambdaUser::getName, // 获取姓名
Collectors.toSet())); // -> Set
List<JavaLambdaUser> userQuerylist = new ArrayList<>();
userList.stream().forEach(user -> {
JavaLambdaUser javaLambdaUser = new JavaLambdaUser();
javaLambdaUser.setName(user.getName());
userQuerylist.add(javaLambdaUser);
});
Map<String, JavaLambdaUser> aMap1 = userList.stream().collect(Collectors.toMap(JavaLambdaUser::getName, Function.identity(), (key1, key2) -> key2, HashMap::new));
Map<String, JavaLambdaUser> partsMap = userList.stream().collect(Collectors.toMap(k -> k.getName() + k.getAge(), v -> v));
Map<String, String> aMap = userList.stream().collect(Collectors.toMap(JavaLambdaUser::getName, JavaLambdaUser::getName));
aMap.forEach( (k, v) -> {System.out.println(k + " " + v);});
}
@Test
public void testSort() {
// 14.按序号降序(TODO:为什么这样反而是降序?)
Collections.sort(userList, (a, b) -> b.getNumber().compareTo(a.getNumber()));
userList.sort(Comparator.comparing(JavaLambdaUser::getNumber).reversed());
System.out.println(userList);
}
@Test
public void testDistinctV1() {
// 15.b. 按性别去重,方法一
List<Integer> genderIds = new ArrayList<>();
List<JavaLambdaUser> distinctList_01 = userList.stream().filter(// 过滤去重
v -> {
boolean flag = !genderIds.contains(v.getGender());
genderIds.add(v.getGender());
return flag;
}
).collect(Collectors.toList());
}
@Test
public void testDistinctV2() {
// 15.a. 按性别去重,方法二
List<JavaLambdaUser> distinctList = userList.stream().collect( // 去重:list -> distinctList
Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(o -> o.getGender()))), // o:代表object对象
ArrayList::new));
// 16.按序号升序
Collections.sort(distinctList, Comparator.comparing(JavaLambdaUser::getNumber));
distinctList.removeIf(e -> "赵六".equals(e.getName()));
System.out.println(distinctList);
}
@Test
public void testIterator() {
Map<String, String> aMap = userList.stream().collect(Collectors.toMap(JavaLambdaUser::getName, JavaLambdaUser::getName));
// 17.迭代的不同实现
for(Map.Entry<String, String> it : aMap.entrySet()) {
System.out.println(it.getKey() + "=" + it.getValue());
System.out.println(it);
}
for(String a : aMap.keySet()) {
System.out.println(a);
}
for(String a : aMap.values()) {
System.out.println(a);
}
for(Iterator<Map.Entry<String, String>> it = aMap.entrySet().iterator(); it.hasNext();) {
Map.Entry<String, String> itt = it.next();
System.out.println(itt.getKey() + "=" + itt.getValue());
System.out.println(itt);
}
}
@Test
public void testPredicate() {
// 18.复杂的Predicate
Predicate<String> startsWithJ = (n) -> n.startsWith("J");
Predicate<String> fourLetterLong = (n) -> n.length() == 4;
List languages = Arrays.asList("Java", "Scala", "C++", "Haskell", "Lisp");
languages.stream().filter(startsWithJ.and(fourLetterLong)).forEach((str) -> System.out.print(str));
}
@Test
public void testStreamMap() {
// 19.遍历
List<Integer> costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
costBeforeTax.stream().map((cost) -> (cost*12))
.forEach(System.out::println);
List<Integer> costBeforeTax1 = Arrays.asList(1, 2, 3, 4, 5);
double bill = costBeforeTax1.stream().map((cost) -> cost + 12*cost)
.reduce((sum, cost) -> sum + cost)
.get();
System.out.println("Total : " + bill);
}
// 从列表中去除重复元素
@Test
public void testDistinct() {
List<Integer> list = Arrays.asList(1, 2, 2, 3, 4, 4, 5);
// 去除重复元素
List<Integer> distinctList = list.stream()
.distinct()
.collect(Collectors.toList());
System.out.println("去重后的列表:" + distinctList);
}
// 合并多个集合
@Test
public void testMergeCollections() {
List<String> list1 = Arrays.asList("a", "b", "c");
List<String> list2 = Arrays.asList("d", "e", "f");
// 合并两个集合
List<String> mergedList = Stream.concat(list1.stream(), list2.stream())
.collect(Collectors.toList());
System.out.println("合并后的集合:" + mergedList);
}
// 分组统计(多级分组)
@Test
public void testMultiLevelGrouping() {
// 按性别和年龄分组统计用户数量
Map<Integer, Map<Integer, Long>> multiLevelGrouping = userList.stream()
.collect(Collectors.groupingBy(
JavaLambdaUser::getGender,
Collectors.groupingBy(
JavaLambdaUser::getAge,
Collectors.counting()
)
));
System.out.println("按性别和年龄分组统计用户数量:" + multiLevelGrouping);
}
// 并行流处理(提高效率)
@Test
public void testParallelStream() {
List<Integer> largeList = new ArrayList<>();
for (int i = 0; i < 1_000_000; i++) {
largeList.add(i);
}
// 使用并行流计算平方和
long startTime = System.currentTimeMillis();
int sumOfSquares = largeList.parallelStream()
.mapToInt(i -> i * i)
.sum();
long endTime = System.currentTimeMillis();
System.out.println("平方和:" + sumOfSquares);
System.out.println("并行流耗时:" + (endTime - startTime) + " ms");
}
// 自定义 Collector 实现
@Test
public void testCustomCollector() {
List<String> list = Arrays.asList("apple", "banana", "cherry");
// 自定义 Collector 实现逗号分隔拼接
String result = list.stream()
.collect(Collector.of(
StringBuilder::new, // 供应器
(sb, s) -> sb.append(s).append(", "), // 累加器
(sb1, sb2) -> sb1.append(sb2), // 组合器
sb -> sb.length() > 0 ? sb.substring(0, sb.length() - 2) : "" // 完成器
));
System.out.println("自定义 Collector 结果:" + result);
}
// 扁平化嵌套集合(Flattening):将嵌套的集合转换为单个集合
@Test
public void testFlatMap() {
List<List<String>> nestedList = Arrays.asList(
Arrays.asList("a", "b"),
Arrays.asList("c", "d"),
Arrays.asList("e", "f")
);
// 扁平化嵌套集合
List<String> flatList = nestedList.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
// 使用断言验证结果
List<String> expectedFlatList = Arrays.asList("a", "b", "c", "d", "e", "f");
assert flatList.equals(expectedFlatList) : "Flattened list does not match expected result";
}
// 条件匹配
@Test
public void testConditionMatching() {
List<Integer> list = Arrays.asList(2, 4, 6, 8);
// 检查所有元素是否为偶数
boolean allEven = list.stream().allMatch(n -> n % 2 == 0);
System.out.println("所有元素是否为偶数:" + allEven);
// 检查是否有元素大于 5
boolean anyGreaterThanFive = list.stream().anyMatch(n -> n > 5);
System.out.println("是否有元素大于 5:" + anyGreaterThanFive);
// 检查是否没有元素小于 0
boolean noneLessThanZero = list.stream().noneMatch(n -> n < 0);
System.out.println("是否没有元素小于 0:" + noneLessThanZero);
}
public static boolean areListsEqualIgnoreOrder(List<String> list1, List<String> list2) {
// 如果大小不同,直接返回 false
if (list1.size() != list2.size()) {
return false;
}
// 使用 Stream API 统计每个列表中元素的出现次数,并比较统计结果
Map<String, Long> frequencyMap1 = list1.stream()
.collect(Collectors.groupingBy(e -> e, Collectors.counting()));
Map<String, Long> frequencyMap2 = list2.stream()
.collect(Collectors.groupingBy(e -> e, Collectors.counting()));
return frequencyMap1.equals(frequencyMap2);
}
private void verifyResult(Map<Integer, Set<String>> result) {
// 定义预期值
Map<Integer, Set<String>> expectedResult = new HashMap<>();
expectedResult.put(1, new HashSet<>(Arrays.asList("李四", "赵六")));
Set<String> set = new HashSet<>();
set.add("张三");
set.add("王五");
expectedResult.put(0, set);
// 使用 assertEquals 验证
assertEquals(expectedResult.size(), result.size());
for (Integer key : expectedResult.keySet()) {
assertTrue(result.containsKey(key));
assertEquals(expectedResult.get(key), result.get(key));
}
}
// 内部类:JavaLambdaUser
@Getter
@Setter
public static class JavaLambdaUser {
private Integer number;
private Integer age;
private Integer gender;
private String name;
private List<String> addresses;
private InnerDetail innerDetail;
// 默认构造函数
public JavaLambdaUser() {}
// 构造函数1
public JavaLambdaUser(int pNumber, String pName, int pAge, int pGender) {
this.number = pNumber;
this.name = pName;
this.age = pAge;
this.gender = pGender;
}
// 构造函数2
public JavaLambdaUser(String pName, int pAge, int pGender) {
this.name = pName;
this.age = pAge;
this.gender = pGender;
}
// 构造函数3
public JavaLambdaUser(String pName, int pAge) {
this.name = pName;
this.age = pAge;
}
// 静态内部类:InnerDetail
@Getter
@Setter
public static class InnerDetail {
private String address;
private String school;
}
}
}
四、总结
通过以上分类介绍和代码示例,我们可以看到Lambda表达式在Java编程中的强大功能。无论是简单的过滤、分组,还是复杂的聚合、并行处理,Lambda表达式都能显著提升代码的简洁性和可读性。
希望本文能帮助你更好地理解和使用Java中的Lambda表达式。如果你有任何疑问或建议,欢迎在评论区留言讨论!