今天给大家分享一下java里的stream api应该如何正确的使用才能真正让我们的代码变得很优雅,因为java推出stream api以后,很多小伙伴觉得特别帅酷炸裂,然后就会在代码里过渡的滥用stream api,最终反而会导致代码变得越来越丑,越来越不清晰,不好维护。
所以大家一定要注意一下stream api的使用,避免滥用,要合理的运用,才能让我们写出一手漂亮的代码来!
在Java的世界里,Stream API无疑是一个让人眼前一亮的特性。它以一种全新的、声明式的方式处理数据集合,使得代码变得更加简洁和易读。然而,有时候开发者会发现,用了Stream之后,代码反而变得不那么美观,甚至有点“丑”。这究竟是怎么回事呢?本文就来探讨一下这个问题。
Java里的Stream是什么?
Stream API是Java 8中引入的一项新特性,它允许你以声明式的方式处理数据集合(包括数组、列表等)。你可以将Stream看作是一种高级迭代器,它提供了一系列的操作,比如筛选、排序、映射等,这些操作可以以一种链式调用的方式组合在一起,从而完成复杂的数据处理任务。
Stream API 的核心概念
- 数据源 (Source): Stream 的数据来源,比如 Collection、数组或其他提供数据的类。
- 中间操作 (Intermediate Operations): 这些操作会创建一个新的 Stream,原 Stream 不变。中间操作通常是惰性的,即它们不会立即执行,直到遇到终止操作才会执行。
- 终止操作 (Terminal Operations): 这些操作会从 Stream 生成结果,执行完后 Stream 就结束了。
Stream API 的使用步骤
- 创建 Stream
- 执行中间操作
- 执行终止操作
中间操作
- filter: 过滤数据。
- map: 映射数据。
- flatMap: 扁平化数据。
- distinct: 去除重复元素。
- sorted: 排序数据。
- peek: 打印或调试数据。
终止操作
- forEach: 遍历数据。
- reduce: 归约数据。
- collect: 收集数据到集合。
- min: 获取最小值。
- max: 获取最大值。
- count: 计算元素数量。
- anyMatch: 是否存在匹配项。
- allMatch: 所有元素是否都匹配。
- noneMatch: 是否没有任何元素匹配。
- findFirst: 获取第一个元素。
- findAny: 获取任意一个元素。
Stream的用法示例
下面是一些使用Stream API的示例,通过这些示例,你可以对Stream的用法有一个基本的了解。
示例1:筛选操作
假设你有一个用户列表,需要筛选出年龄大于18岁的用户。
List<User> users = Arrays.asList( new User("Alice", 20), new User("Bob", 17), new User("Charlie", 22) ); List<User> adults = users.stream() .filter(user -> user.getAge() > 18) .collect(Collectors.toList());
在这个示例中,filter
方法用于筛选年龄大于18岁的用户,collect
方法用于将筛选结果收集到一个新的列表中。
示例2:排序操作
假设你需要将用户列表按照年龄进行排序。
List<User> sortedUsers = users.stream() .sorted(Comparator.comparingInt(User::getAge)) .collect(Collectors.toList());
在这个示例中,sorted
方法用于对用户列表进行排序,排序的依据是用户的年龄。
示例3:映射操作
假设你需要从用户列表中提取出所有用户的姓名。
List<String> names = users.stream() .map(User::getName) .collect(Collectors.toList());
map
方法用于提取用户的姓名,并将提取结果收集到一个新的列表中。
为什么用了Stream以后,代码反而越写越丑?
虽然Stream API提供了很多便利的操作,但是如果使用不当,就会导致代码变得难以阅读和维护。下面是一些可能导致代码变“丑”的原因:
1、过度使用:有些开发者为了追求代码的“函数式”,不管三七二十一,什么地方都用Stream,结果导致代码变得难以阅读。比如,一个简单的for循环就能解决的问题,偏偏要用Stream来实现,结果代码行数翻倍,可读性大大降低。
2、链式调用过长:Stream API支持链式调用,这是一个非常强大的特性。但是,如果链式调用过长,就会导致一行代码变得非常长,甚至超过屏幕的宽度,这显然是不利于阅读的。
3、滥用Lambda表达式:Lambda表达式是Stream API的常用搭档,它让代码变得更加简洁。但是,如果滥用Lambda表达式,比如在一个Lambda表达式中嵌套另一个Lambda表达式,就会导致代码变得难以理解和维护。
4、忽视代码的可读性:有些开发者在使用Stream时,过分追求代码的简洁性,而忽视了代码的可读性。他们可能会把多个操作合并到一行代码中,或者使用一些不太常见的Stream操作,导致其他开发者难以理解代码的含义。
下面是一个代码变“丑”的示例:
List<String> result = users.stream() .filter(user -> user.getAge() > 18) .map(user -> { String name = user.getName(); return name.toUpperCase(); }) .sorted((name1, name2) -> name1.compareTo(name2)) .collect(Collectors.toList());
虽然使用了Stream API完成了任务,但是代码的可读性并不好。链式调用过长,且Lambda表达式中嵌套了另一个Lambda表达式,这使得代码难以理解和维护。
如何合理用Stream才能让代码保持优雅?
要避免代码变“丑”,我们需要合理使用Stream API。下面是一些建议:
1、不要过度使用:不是所有的地方都适合使用Stream,比如简单的遍历和修改集合元素的操作,使用传统的for循环或者for-each循环可能更加直观和简洁。因此,在使用Stream之前,先思考一下是否真的需要它。
2、控制链式调用的长度:虽然Stream支持链式调用,但是并不意味着你可以无限制地链式下去。当链式调用过长时,可以考虑将其拆分成多个步骤,每个步骤都使用一行代码来表示,这样可以提高代码的可读性。
3、慎用Lambda表达式:Lambda表达式确实可以让代码变得更加简洁,但是并不是所有的情况都适合使用它。比如,当一个Lambda表达式过于复杂时,可以考虑将其拆分成一个独立的方法,这样既可以保持代码的简洁性,又可以提高代码的可读性。
4、注重代码的可读性:在使用Stream时,一定要注重代码的可读性。不要为了追求代码的简洁性而牺牲可读性。比如,可以使用一些有意义的变量名来替代复杂的Lambda表达式,或者使用注释来解释一些不太直观的操作。
下面是一个合理使用Stream的示例:
// 先筛选年龄大于18岁的用户 List<User> adults = users.stream() .filter(user -> user.getAge() > 18) .collect(Collectors.toList()); // 再提取用户的姓名,并转换为大写 List<String> names = adults.stream() .map(User::getName) .map(String::toUpperCase) .collect(Collectors.toList()); // 最后对姓名进行排序 List<String> sortedNames = names.stream() .sorted() .collect(Collectors.toList());
在这个示例中,我们将任务拆分成了多个步骤,每个步骤都使用了一行代码来表示。这样,代码的可读性就大大提高了。同时,我们也避免了过度使用Stream和滥用Lambda表达式的问题。
结语
总的来说,Stream API是一个非常强大的特性,它可以让代码变得更加简洁和易读。但是,如果使用不当,就会导致代码变得“丑”。因此,在使用Stream时,我们需要保持一定的克制和审慎,确保代码的可读性和可维护性。只有这样,我们才能充分利用Stream API的优势,写出更加优雅和高效的代码。