专栏名称: Java基基
一个苦练基本功的 Java 公众号,所以取名 Java 基基
目录
相关文章推荐
生物学霸  ·  Nature 推荐的文献检索 AI ... ·  2 天前  
槽值  ·  杀疯了的“三亚平替”,挤满东北人 ·  2 天前  
BioArt  ·  Cell Metab | ... ·  2 天前  
51好读  ›  专栏  ›  Java基基

为什么用了Stream,代码反而越写越丑了?

Java基基  · 公众号  ·  · 2025-02-19 11:55

正文

👉 这是一个或许对你有用 的社群

🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入 芋道快速开发平台 知识星球。 下面是星球提供的部分资料:

👉 这是一个或许对你有用的开源项目

国产 Star 破 10w+ 的开源项目,前端包括管理后台 + 微信小程序,后端支持单体和微服务架构。

功能涵盖 RBAC 权限、SaaS 多租户、数据权限、 商城 、支付、工作流、大屏报表、微信公众号、 ERP CRM AI 大模型 等等功能:

  • Boot 多模块架构:https://gitee.com/zhijiantianya/ruoyi-vue-pro
  • Cloud 微服务架构:https://gitee.com/zhijiantianya/yudao-cloud
  • 视频教程:https://doc.iocoder.cn
【国内首批】支持 JDK 17/21 + SpringBoot 3.3、JDK 8/11 + Spring Boot 2.7 双版本

来源:网络


我们常常遇到的一个问题:用了 Stream 后,代码反而越来越丑了?明明说好的“优雅”和“简洁”呢?怎么写着写着,代码越来越像拼图游戏,一块儿接不上另一块,错落不堪?

作为程序员,我们都希望代码简洁、优雅、易于维护, Stream Lambda 就是为了这个目的而生的,它们一度被视为能让代码焕发光彩的神兵利器。

但实际使用中,我们发现, Stream Lambda 的魅力不总是那么简单,反而成了许多开发者的“陷阱”。

今天,就让我们从程序员的视角,深扒一下这些“优雅工具”到底是怎么从神器变成了累赘的。

1. Stream 和 Lambda:优雅的真面目,还是滥用的根源?

Stream Lambda 一开始确实是给我们的代码带来了不少福利,尤其是在代码简洁性和功能扩展方面。你想想,几行代码就能搞定一个复杂的集合操作,像极了魔法对吧?特别是 Lambda 表达式,那种不再需要写匿名类的写法,简直让人心情愉悦。

Stream 的优势

  • 简洁性: Stream 允许你链式调用,可以避免大量的 for 循环嵌套,让代码看起来更简洁明了。
  • 功能扩展灵活: 只要你会组合各种操作符( filter , map , reduce 等),几乎可以用 Stream 做任何你想做的事情。

但是——这里有个大问题,那就是滥用。很多时候, Stream Lambda 被当成了“随便写的工具”,没有考虑到代码的可读性和维护性。想象一下,当你看到下面这段代码时,你是什么感受?

List result = list.stream()
    .filter(x -> x.length() > 5)
    .map(x -> x.toUpperCase())
    .filter(x -> x.contains('A'))
    .reduce('', (s1, s2) -> s1 + s2);

看起来很简洁对吧?但你仔细想想,这么一连串的操作,谁能在两秒钟内理解这段代码的含义?如果抛出个异常,栈信息看起来简直像乱炖。😓

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 视频教程:https://doc.iocoder.cn/video/

2. 代码优化技巧:让代码既简洁又好懂

想要避免滥用,我们就得讲究一些技巧,让代码在简洁的同时,也不失可读性。

合理的换行

很多人把 Stream 链式调用堆在一行里,导致代码难以阅读。其实,这时候换行是非常有必要的,尤其是在涉及多个操作符的时候。以下是优化后的代码:

List result = list.stream()
    .filter(x -> x.length() > 5)
    .map(x -> x.toUpperCase())
    .filter(x -> x.contains('A'))
    .reduce('', (s1, s2) -> s1 + s2);

这样拆开后,代码的层次感更强,也方便我们理解每一部分的功能。甚至,关键的操作我们还可以分到独立的方法里,使得每个函数只做一件事,避免一个方法承担过多职责。

拆分函数

当你遇到复杂逻辑时,不要抱着“懒”字当头,把所有的代码都塞进一个方法里。合理拆分函数是提高代码可维护性的好习惯,特别是对于像 Stream 这样本来就容易堆积复杂逻辑的情况。

比如,我们可以将复杂的 filter 条件提取成一个单独的 Predicate

public static Predicate isValidLength() {
    return x -> x.length() > 5;
}

public static Predicate containsA() {
    return x -> x.contains('A');
}

// 然后在 Stream 中调用
List result = list.stream()
    .filter(isValidLength())
    .map(String::toUpperCase)
    .filter(containsA())
    .reduce('', (s1, s2) -> s1 + s2);

这样不仅提高了可读性,还能增加代码的复用性。让每个函数更专注于自己的职责,也让 Stack Trace 更加清晰。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/yudao-cloud
  • 视频教程:https://doc.iocoder.cn/video/

3. 避免逻辑堆积:过滤器里复杂逻辑还是要小心

说到 Stream ,我们都知道 filter 是一个常用的操作,它可以帮助我们根据条件筛选数据。但如果条件复杂了,直接把所有逻辑写在 filter 里,往往会让代码看起来“过于密集”。这样做不仅降低了代码的可读性,还可能导致理解和维护上的困难。

比如,假设你有一个复杂的条件判断:

List result = list.stream()
    .filter(x -> {
        if (x.length() > 5) {
            if (x.contains('A')) {
                return true;
            }
        }
        return false;
    })
    .collect(Collectors.toList());

这种做法让代码看起来复杂且不易扩展。可以将条件逻辑提取到一个单独的方法,传递一个清晰的 Predicate filter

public static boolean isValid(String x) {
    return x.length() > 5 && x.contains('A');
}

// 然后使用
List result = list.stream()
    .filter(MyClass::isValid)
    .collect(Collectors.toList());

这样写,代码就更加简洁,而且每个条件都有明确的定义和单独的关注点。以后增加条件时也方便得多。

4. Optional:这事儿其实可以做得更优雅

Optional 是 Java 8 引入的一个特性,主要用来避免空指针异常。大部分情况下,使用 Optional 的确是个好习惯,但是大家往往会犯一个大忌——滥用 Optional.get()

当你直接调用 Optional.get() 时,如果值是 null,会抛出 NoSuchElementException ,这不是你想要的结果。相反,使用 map orElse 等方法能避免这种问题:







请到「今天看啥」查看全文