原文链接:https://medium.com/javarevisited/10-java-stream-tips-must-read-2063a84af3be
作者&文章简介
- 作者:Sivaram Rasathurai
- 开篇作者形容
Java Stream API
就像是瑞士军刀一般,小巧&紧凑,可以处理多种多样的任务 Java Stream API
为函数式编程风格,能写出无副作用的函数,使代码可以更加简洁的表现- 如果想用好
Stream API
,需要了解他的最佳实践以及常见的陷阱 此篇文章中,作者将带领大家了解
Stream API
的十条最佳实践Java Stream API 的十条最佳实践
1、原始类型的 Stream 流性能更优
int ->
IntStream
- long ->
LongStream
- double ->
DoubleStream
推荐理由:上面的几种原始类型的 Stream 可以避免额外的装箱和拆箱操作
public static void main(String[] args) {
int[] array = new int[]{1, 2, 3, 4, 5};
StopWatch sw1 = new StopWatch();
sw1.start();
for (int i = 0; i < 100 * 10000; i++) {
Arrays.stream(array).sum();
}
sw1.stop();
System.out.println("Arrays.stream : " + sw1.getTotalTimeMillis());
StopWatch sw2 = new StopWatch();
sw2.start();
for (int i = 0; i < 100 * 10000; i++) {
IntStream.of(array).sum();
}
sw2.stop();
System.out.println("IntStream : " + sw2.getTotalTimeMillis());
}
// 分别进行 100 万次计算,结果如下(毫秒):
// Arrays.stream : 104
// IntStream : 29
2、避免嵌套的 Stream 流
推荐理由:嵌套的 Stream 不易读,建议分而治之,将中间结果存储在临时变量中
public static void main(String[] args) {
List<String> list1 = Arrays.asList("apple", "banana", "cherry");
List<String> list2 = Arrays.asList("orange", "pineapple", "mango");
List<String> result = Stream.concat(list1.stream(), list2.stream())
.filter(s -> s.length() > 5)
.collect(Collectors.toList());
}
3、谨慎使用并行流
推荐理由:虽然并行 Stream 流能够提升性能,但是也会带来额外的开销以及条件竞争
我们在使用 parallelStream
的时候要注意数据的大小、操作的复杂度、以及并行数
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
int sum = list.parallelStream().reduce(0, Integer::sum);
}
4、惰性求值性能更优
推荐理由:在终端操作被调用前,那些中间操作过程不会被执行
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> result = list.stream()
.filter(n -> n > 3)
.findFirst();
}
5、可以避免副作用
推荐理由:**Stream API**
是为集合数据运算而设计,应避免修改外部变量等操作
public static void main(String[] args) {
List<String> list = Arrays.asList("apple", "banana", "cherry");
long count = 0;
list.stream()
.filter(s -> s.startsWith("a"))
.forEach(s -> count++);
// 这里的 count++ 会报错:
// Variable used in lambda expression should be final or effectively final
}
public static void main(String[] args) {
List<String> list = Arrays.asList("apple", "banana", "cherry");
long count = list.stream()
.filter(s -> s.startsWith("a"))
.mapToLong(e -> 1L)
.sum();
}
6、使用不可变对象
推荐理由:可以确保在操作过程中对象没有发生修改,可以在阅读代码过程中更加准确的预测其行为
public static void main(String[] args) {
List<String> list = Arrays.asList("apple", "banana", "cherry");
List<String> result = list.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
}
7、在 map() 前使用 filter() 过滤非必要的数据
推荐理由:特别是处理数据量较大的集合时,先过滤再映射能够提升不少性能
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> filteredList = list.stream()
.filter(i -> i % 2 == 0)
.map(i -> i * 2)
.collect(Collectors.toList());
}
8、方法引用 优于 Lambda 表达式
推荐理由:使用方法引用比使用 Lambda 表达式更简洁易读
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
int sum = list.stream()
.reduce(0, Integer::sum);
}
9、使用 distinct() 移除重复项
推荐理由:如小标题所示,在你不需要重复项时可以使用 distinct() 来过滤
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 3, 4, 5, 5);
List<Integer> distinctList = list.stream()
.distinct()
.collect(Collectors.toList());
}
10、谨慎使用 sorted()
推荐理由:sorted() 操作消耗较大,特别是要操作的集合数较多时,建议仅当需要时再用
public static void main(String[] args) {
List<Integer> list = Arrays.asList(3, 2, 1);
List<Integer> sortedList = list.stream()
.sorted()
.collect(Collectors.toList());
}
扩展阅读
- 我在 Java 知识大纲下整理的 Stream API 思维导图:
- 《Java8实战》第二部分:函数式数据处理