Java Streams 是 Java 8 引入后,彻底改变了开发者处理集合的方式。Java Streams 已经成为处理集合的必备工具。它们不仅让数据处理变得更加简洁、易读,还显著提升了代码的可维护性和开发者的生产力。尽管 filtermap 是基本操作,但 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 提取每个 Testersoul,生成一个 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 都能为你提供无穷的可能性,让你的编程技巧更上一层楼。