Java Stream 教程:深入探索与实践
简介
Java Stream 是 Java 8 引入的一个强大功能,它为处理集合数据提供了一种简洁、高效且声明式的方式。Stream 允许你以函数式编程风格对数据集进行操作,使代码更加紧凑、易读,同时提升性能。本教程将深入介绍 Java Stream 的基础概念、使用方法、常见实践以及最佳实践,帮助你熟练掌握这一强大工具。
目录
- 基础概念
- 使用方法
- 创建 Stream
- 中间操作
- 终止操作
- 常见实践
- 过滤数据
- 映射数据
- 归约操作
- 排序操作
- 最佳实践
- 性能优化
- 代码可读性
- 并行 Stream
- 小结
- 参考资料
基础概念
Stream 是一系列支持顺序和并行聚合操作的元素。它不是数据结构,不存储数据,而是从数据源(如集合、数组)获取数据,并在其上进行各种操作。Stream 操作分为中间操作和终止操作:
- 中间操作:返回一个新的 Stream,可链式调用多个中间操作。例如 filter
、map
等。
- 终止操作:执行 Stream 操作链,并返回结果。例如 forEach
、collect
等。
使用方法
创建 Stream
- 从集合创建
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class StreamCreation {
public static void main(String[] args) {
List<String> list = Arrays.asList("apple", "banana", "cherry");
Stream<String> streamFromList = list.stream();
Stream<String> parallelStreamFromList = list.parallelStream();
}
}
- 从数组创建
import java.util.stream.Stream;
public class ArrayStream {
public static void main(String[] args) {
String[] array = {"one", "two", "three"};
Stream<String> streamFromArray = Arrays.stream(array);
}
}
- 使用
Stream.of
创建
import java.util.stream.Stream;
public class StreamOf {
public static void main(String[] args) {
Stream<String> stream = Stream.of("a", "b", "c");
}
}
中间操作
- 过滤数据:使用
filter
方法根据条件过滤元素。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class FilterExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println(evenNumbers); // 输出: [2, 4, 6]
}
}
- 映射数据:使用
map
方法将元素转换为另一种形式。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class MapExample {
public static void main(String[] args) {
List<String> words = Arrays.asList("hello", "world");
List<Integer> wordLengths = words.stream()
.map(String::length)
.collect(Collectors.toList());
System.out.println(wordLengths); // 输出: [5, 5]
}
}
- 去重:使用
distinct
方法去除重复元素。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class DistinctExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 4, 4, 5);
List<Integer> distinctNumbers = numbers.stream()
.distinct()
.collect(Collectors.toList());
System.out.println(distinctNumbers); // 输出: [1, 2, 3, 4, 5]
}
}
终止操作
- 遍历元素:使用
forEach
方法对每个元素执行操作。
import java.util.Arrays;
import java.util.List;
public class ForEachExample {
public static void main(String[] args) {
List<String> fruits = Arrays.asList("apple", "banana", "cherry");
fruits.stream()
.forEach(System.out::println);
}
}
- 收集结果:使用
collect
方法将 Stream 结果收集到集合中。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class CollectExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> squaredNumbers = numbers.stream()
.map(n -> n * n)
.collect(Collectors.toList());
System.out.println(squaredNumbers); // 输出: [1, 4, 9, 16, 25]
}
}
- 归约操作:使用
reduce
方法将 Stream 中的元素归约为一个值。
import java.util.Arrays;
import java.util.List;
public class ReduceExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
.reduce(0, Integer::sum);
System.out.println(sum); // 输出: 15
}
}
常见实践
过滤数据
在处理大量数据时,常常需要根据特定条件过滤出符合要求的数据。例如,从用户列表中筛选出年龄大于 18 岁的用户。
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
}
public class UserFilterExample {
public static void main(String[] args) {
List<User> users = new ArrayList<>();
users.add(new User("Alice", 16));
users.add(new User("Bob", 20));
users.add(new User("Charlie", 22));
List<User> adultUsers = users.stream()
.filter(user -> user.getAge() > 18)
.collect(Collectors.toList());
System.out.println(adultUsers.size()); // 输出: 2
}
}
映射数据
将一种类型的数据转换为另一种类型是常见的需求。比如,将字符串列表中的每个字符串转换为大写形式。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StringMapExample {
public static void main(String[] args) {
List<String> words = Arrays.asList("hello", "world");
List<String> upperCaseWords = words.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(upperCaseWords); // 输出: [HELLO, WORLD]
}
}
归约操作
计算集合中元素的总和、乘积等操作可以使用归约操作。例如,计算整数列表的乘积。
import java.util.Arrays;
import java.util.List;
public class ProductReduceExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
int product = numbers.stream()
.reduce(1, (a, b) -> a * b);
System.out.println(product); // 输出: 24
}
}
排序操作
对集合中的元素进行排序也是常见的操作。可以使用 sorted
方法对 Stream 中的元素进行排序。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class SortExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5, 9);
List<Integer> sortedNumbers = numbers.stream()
.sorted()
.collect(Collectors.toList());
System.out.println(sortedNumbers); // 输出: [1, 1, 3, 4, 5, 9]
}
}
最佳实践
性能优化
- 使用并行 Stream:对于大规模数据集,并行 Stream 可以充分利用多核处理器的优势,提高处理速度。但要注意并行操作可能带来的线程安全问题。
- 避免不必要的中间操作:尽量减少 Stream 操作链中的中间操作,以减少计算开销。
代码可读性
- 保持操作链简洁:避免过长的操作链,可将复杂操作拆分成多个方法,提高代码的可读性和维护性。
- 使用描述性的方法名:在自定义方法时,使用清晰、描述性的方法名,使代码意图一目了然。
并行 Stream
在使用并行 Stream 时,确保数据源是线程安全的。如果数据源不是线程安全的,可能会导致数据不一致或其他错误。例如,在多线程环境下对共享集合进行写操作时,应使用线程安全的集合类。
小结
Java Stream 为处理集合数据提供了一种强大而灵活的方式。通过理解基础概念、掌握使用方法以及遵循最佳实践,你可以编写出高效、易读的代码。Stream 的中间操作和终止操作使得数据处理变得简洁明了,无论是过滤、映射、归约还是排序等操作,都能轻松实现。希望本教程能帮助你在日常开发中熟练运用 Java Stream,提升开发效率。