Java Streams 是 Java 8 引入后,彻底改变了开发者处理集合的方式。Java Streams 已经成为处理集合的必备工具。它们不仅让数据处理变得更加简洁、易读,还显著提升了代码的可维护性和开发者的生产力。尽管 filter
和 map
是基本操作,但 Java Streams API 中还有很多值得探索的高级用法。
在这篇文章中,我将介绍 5 个实用技巧,帮助你更好地运用 Java Streams。这些方法不仅能简化复杂的数据转换,还能让你的代码更加清晰和高效。
精准过滤
入门
想象一下,你有一个产品列表,想要筛选出有趣的灵魂测试工程师。filter
操作是实现这一目标的利器。来看个例子:
public static void main(String[] args) {
List<FunTester> testers = new ArrayList<>();
List<FunTester> funTesters = testers.stream()
.filter(tester -> tester.getSoul().contains("Fun"))
.collect(Collectors.toList());
}
这段代码从 testers
列表中创建了一个流,并通过 filter
筛选出有趣的灵魂(Teseter),最后将结果收集到一个新的 funTesters
列表中。
进阶
filter
的强大之处在于你可以轻松地组合多个条件。例如,如果你还想筛选出特定类别(比如成年的),可以再加一个 filter
:
public static void main(String[] args) {
List<FunTester> testers = new ArrayList<>();
List<FunTester> funTesters = testers.stream()
.filter(tester -> tester.getSoul().contains("Fun"))
.filter(tester -> tester.getAge() > 18)
.collect(Collectors.toList());
}
通过组合多个条件,你可以更加精准地获取需要的结果。
map 转换
入门
map
操作可以对流中的每个元素进行转换。它接收一个函数(通常是 Lambda
表达式),将其应用于每个元素,生成一个包含转换结果的新流。
来看个简单的例子:
public static void main(String[] args) {
List<FunTester> testers = new ArrayList<>();
List<String> souls = testers.stream()
.map(FunTester::getSoul)
.collect(Collectors.toList());
}
在这个例子中,我们使用 map
提取每个 Tester
的 soul
,生成一个 String
的列表。
进阶
我们可以在 map
之前或之后添加 filter 操作,来筛选和转换数据。例如,只处理非空的 soul 属性:
public static void main(String[] args) {
List<FunTester> testers = new ArrayList<>();
List<String> souls = testers.stream()
.map(FunTester::getSoul)
.filter(Objects::nonNull) // 过滤掉空值
.map(String::trim) // 去除首尾空格
.collect(Collectors.toList());
}
map
的用途非常广泛,不仅限于简单的数据提取,还能用于更复杂的转换。
汇总
入门
reduce
操作可以将流中的元素累积成一个单一结果,非常适合进行统计或数据汇总。比如,计算所有 Tester 的 BUG 总数:
public static void main(String[] args) {
List<FunTester> testers = new ArrayList<>();
int bugs = testers.stream()
.mapToInt(FunTester::getBugs)// 获取所有测试工程师的 bug 数量
.reduce(0, Integer::sum);// 计算总 bug 数量
}
这段代码演示了如何使用 Java Streams API 高效地计算 List 中所有 FunTester 对象的 BUG 总数。使用 mapToInt 提取 int 值并通过 reduce 计算总和是处理这类问题的常见模式。这种方法简洁而强大,适用于各种类似的数据处理场景。
进阶
reduce
还能用于其他类型的汇总操作。比如,找出最便宜的商品:
FunTester funTester = testers.stream()
.reduce((t1, t2) -> t1.getBugs() > t2.getBugs() ? t1 : t2)
.get();
System.out.println("BUG 最多的灵魂测试工程师是:" + funTester.getName());
在这个例子中,reduce
用于比较每个 Tester 的 BUG 数量,找出 BUG 最多的那个,把他揪出来。
分组
基础
groupBy 是 Java Streams API 中的一个强大功能,它允许你根据某个特性将流中的元素分组。通过 groupBy,你可以将具有相似属性的元素归类到同一个集合中,从而方便地进行进一步的分析和处理。
比如将 Tester 根据司龄进行分组:
Map<Integer, List<FunTester>> collect = testers.stream()
.collect(Collectors.groupingBy(FunTester::getCompanyYears));
代码演示了如何使用 Java Streams API 将 FunTester 对象按司龄(getCompanyYears
)进行分组。以下是完整示例,展示了如何按工作年限对 FunTester 对象进行分组,并输出分组结果。
进阶
Java Streams 甚至支持嵌套分组。例如可以先根据司龄进行分组,然后再根据实际年龄分组。
public static void main(String[] args) {
List<FunTester> testers = new ArrayList<>();
Map<Integer, Map<String, List<FunTester>>> collect = testers.stream()
.collect(Collectors.groupingBy(FunTester::getCompanyYears, Collectors.groupingBy(t -> {
if (t.getAge() > 35) {// 年龄大于35岁,则分组
return "年龄大";
} else {
return "年龄小";
}
})));
}
通过嵌套的 groupingBy
,你可以将数据进行多层次的分组,使得组织数据更为灵活。
Java Streams 的潜力
Java Streams 的强大功能远不止于此。随着你深入探索这个 API 的更多特性,你会发现它不仅能帮助你处理集合数据,还能极大地提升代码的可读性和维护性。通过不断实践和应用,你将逐渐掌握这些工具的精髓,使得代码变得更加高效、简洁、优雅。不论是处理复杂的数据转换,还是实现更灵活的操作,Java Streams 都能为你提供无穷的可能性,让你的编程技巧更上一层楼。