深入理解 Java Stream
简介
在 Java 编程中,Stream 是一个强大的功能,它提供了一种更简洁、高效的方式来处理集合数据。Stream 允许你以声明式的方式处理数据序列,而不必编写复杂的循环和条件语句。本文将详细介绍 Java Stream 的基础概念、使用方法、常见实践以及最佳实践,帮助你更好地掌握和运用这一特性。
目录
- 基础概念
- 使用方法
- 创建 Stream
- 中间操作
- 终端操作
- 常见实践
- 过滤数据
- 映射数据
- 聚合操作
- 排序
- 最佳实践
- 避免不必要的装箱和拆箱
- 合理使用并行 Stream
- 注意 Stream 的短路操作
- 小结
- 参考资料
基础概念
Java Stream 是 Java 8 引入的新特性,它代表了一系列支持顺序和并行聚合操作的元素。Stream 并不是数据结构,它不会存储数据,而是提供了一种对数据源进行操作的方式。数据源可以是集合、数组、文件等。Stream 的操作可以分为中间操作和终端操作,中间操作返回一个新的 Stream,终端操作执行计算并返回结果。
使用方法
创建 Stream
-
从集合创建 ```java import java.util.ArrayList; import java.util.List; import java.util.stream.Stream;
public class StreamExample { public static void main(String[] args) { List
list = new ArrayList<>(); list.add("apple"); list.add("banana"); list.add("cherry"); // 创建顺序 Stream Stream<String> stream = list.stream(); // 创建并行 Stream Stream<String> parallelStream = list.parallelStream(); }
}
2. **从数组创建**
java import java.util.stream.Stream;public class ArrayStreamExample { public static void main(String[] args) { String[] array = {"apple", "banana", "cherry"}; Stream
stream = Stream.of(array); } } ```
中间操作
-
过滤(filter):用于筛选出符合条件的元素。 ```java import java.util.ArrayList; import java.util.List; import java.util.stream.Stream;
public class FilterExample { public static void main(String[] args) { List
numbers = new ArrayList<>(); numbers.add(1); numbers.add(2); numbers.add(3); numbers.add(4); numbers.add(5); Stream<Integer> filteredStream = numbers.stream() .filter(n -> n % 2 == 0); }
}
2. **映射(map)**:将一个元素转换为另一个元素。
java import java.util.ArrayList; import java.util.List; import java.util.stream.Stream;public class MapExample { public static void main(String[] args) { List
words = new ArrayList<>(); words.add("apple"); words.add("banana"); words.add("cherry"); Stream<Integer> lengthStream = words.stream() .map(String::length); }
}
3. **去重(distinct)**:去除 Stream 中的重复元素。
java import java.util.ArrayList; import java.util.List; import java.util.stream.Stream;public class DistinctExample { public static void main(String[] args) { List
numbers = new ArrayList<>(); numbers.add(1); numbers.add(2); numbers.add(2); numbers.add(3); numbers.add(3); Stream<Integer> distinctStream = numbers.stream() .distinct(); }
} ```
终端操作
-
遍历(forEach):对 Stream 中的每个元素执行指定的操作。 ```java import java.util.ArrayList; import java.util.List; import java.util.stream.Stream;
public class ForEachExample { public static void main(String[] args) { List
list = new ArrayList<>(); list.add("apple"); list.add("banana"); list.add("cherry"); list.stream() .forEach(System.out::println); }
}
2. **收集(collect)**:将 Stream 中的元素收集到一个集合中。
java import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream;public class CollectExample { public static void main(String[] args) { List
numbers = new ArrayList<>(); numbers.add(1); numbers.add(2); numbers.add(3); numbers.add(4); numbers.add(5); List<Integer> evenNumbers = numbers.stream() .filter(n -> n % 2 == 0) .collect(Collectors.toList()); }
}
3. **归约(reduce)**:将 Stream 中的元素组合成一个值。
java import java.util.ArrayList; import java.util.List; import java.util.stream.Stream;public class ReduceExample { public static void main(String[] args) { List
numbers = new ArrayList<>(); numbers.add(1); numbers.add(2); numbers.add(3); numbers.add(4); numbers.add(5); int sum = numbers.stream() .reduce(0, Integer::sum); }
} ```
常见实践
过滤数据
在处理集合数据时,经常需要根据某些条件过滤出符合要求的元素。例如,从一个员工列表中筛选出年龄大于 30 岁的员工:
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
class Employee {
private String name;
private int age;
public Employee(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
}
public class EmployeeFilterExample {
public static void main(String[] args) {
List<Employee> employees = new ArrayList<>();
employees.add(new Employee("Alice", 25));
employees.add(new Employee("Bob", 35));
employees.add(new Employee("Charlie", 40));
Stream<Employee> filteredEmployees = employees.stream()
.filter(e -> e.getAge() > 30);
}
}
映射数据
映射操作可以将一种类型的元素转换为另一种类型。例如,将一个字符串列表中的每个字符串转换为大写形式:
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class StringMapExample {
public static void main(String[] args) {
List<String> words = new ArrayList<>();
words.add("apple");
words.add("banana");
words.add("cherry");
Stream<String> upperCaseWords = words.stream()
.map(String::toUpperCase);
}
}
聚合操作
聚合操作可以对 Stream 中的元素进行计算,如求和、求平均值、最大值、最小值等。例如,计算一个整数列表的总和:
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class AggregationExample {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);
int sum = numbers.stream()
.mapToInt(Integer::intValue)
.sum();
}
}
排序
可以对 Stream 中的元素进行排序。例如,对一个整数列表进行升序排序:
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class SortingExample {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(5);
numbers.add(3);
numbers.add(1);
numbers.add(4);
numbers.add(2);
Stream<Integer> sortedStream = numbers.stream()
.sorted();
}
}
最佳实践
避免不必要的装箱和拆箱
在使用 Stream 时,尽量使用原始类型的 Stream,如 IntStream、LongStream、DoubleStream 等,以避免不必要的装箱和拆箱操作,提高性能。
import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;
public class PrimitiveStreamExample {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);
// 使用 IntStream 避免装箱和拆箱
IntStream intStream = numbers.stream()
.mapToInt(Integer::intValue);
}
}
合理使用并行 Stream
并行 Stream 可以利用多核处理器提高处理速度,但并非所有场景都适合使用并行 Stream。在使用并行 Stream 时,要考虑数据量、计算复杂度以及是否存在共享状态等因素。例如,对于小数据量的操作,并行 Stream 可能反而会带来额外的开销。
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class ParallelStreamExample {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);
// 合理使用并行 Stream
Stream<Integer> parallelStream = numbers.parallelStream()
.filter(n -> n % 2 == 0);
}
}
注意 Stream 的短路操作
一些终端操作(如 findFirst、anyMatch 等)是短路操作,即一旦满足条件就会停止处理。在编写代码时,可以利用这些短路操作提高效率。
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class ShortCircuitExample {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);
boolean anyEven = numbers.stream()
.anyMatch(n -> n % 2 == 0);
}
}
小结
Java Stream 为处理集合数据提供了一种强大而简洁的方式。通过理解基础概念、掌握使用方法、熟悉常见实践以及遵循最佳实践,你可以在编写代码时更加高效地利用 Stream,提高代码的可读性和性能。希望本文能够帮助你深入理解并熟练运用 Java Stream。