Java 8 引入了 Stream API,使得我们可以更加简洁、高效地处理集合。Stream API 提供了一种函数式编程风格的操作集合的方法,使代码更简洁、可读性更高。本文将详细介绍 Java Stream API 的基本用法和一些实践技巧。
什么是 Stream API
Stream 是 Java 8 引入的一个新的抽象,它代表一个支持数据处理操作的序列。Stream 以函数式编程的方式处理数据,可以执行过滤、映射、规约等操作。与传统的集合不同,Stream 不存储数据,而是按需计算。
Stream 的创建方式
可以通过多种方式创建 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("a", "b", "c");
Stream<String> streamFromList = list.stream();
// 从数组创建
String[] array = {"x", "y", "z"};
Stream<String> streamFromArray = Arrays.stream(array);
// 使用 Stream.of 创建
Stream<String> streamOf = Stream.of("1", "2", "3");
// 创建无限流
Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 2);
// 打印流中的元素
streamFromList.forEach(System.out::println);
streamFromArray.forEach(System.out::println);
streamOf.forEach(System.out::println);
infiniteStream.limit(5).forEach(System.out::println);
}
}
常见的中间操作
中间操作返回一个新的 Stream,对原有 Stream 进行处理。常见的中间操作有 filter、map、sorted 和 distinct 等。
filter - 过滤元素
filter 方法用于过滤元素:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamFilterExample {
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 - 映射元素
map 方法用于将元素映射为新的元素:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamMapExample {
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]
}
}
sorted - 排序元素
sorted 方法用于对元素进行排序:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamSortedExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(5, 3, 1, 4, 2);
List<Integer> sortedNumbers = numbers.stream()
.sorted()
.collect(Collectors.toList());
System.out.println(sortedNumbers); // 输出: [1, 2, 3, 4, 5]
}
}
distinct - 去重元素
distinct 方法用于去除重复元素:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamDistinctExample {
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]
}
}
常见的终端操作
终端操作会触发 Stream 的执行,并返回一个结果。常见的终端操作有 forEach、collect、reduce 和 count 等。
forEach - 遍历元素
forEach 方法用于遍历每一个元素:
import java.util.Arrays;
import java.util.List;
public class StreamForEachExample {
public static void main(String[] args) {
List<String> items = Arrays.asList("a", "b", "c");
items.stream().forEach(System.out::println);
}
}
collect - 收集元素
collect 方法用于将 Stream 的元素收集到一个集合中:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamCollectExample {
public static void main(String[] args) {
List<String> items = Arrays.asList("a", "b", "c");
List<String> collectedItems = items.stream()
.collect(Collectors.toList());
System.out.println(collectedItems); // 输出: [a, b, c]
}
}
reduce - 规约元素
reduce 方法用于将元素组合起来:
import java.util.Arrays;
import java.util.List;
public class StreamReduceExample {
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
}
count - 统计元素个数
count 方法用于计算元素的个数:
import java.util.Arrays;
import java.util.List;
public class StreamCountExample {
public static void main(String[] args) {
List<String> items = Arrays.asList("a", "b", "c");
long count = items.stream().count();
System.out.println(count); // 输出: 3
}
}
实战技巧
并行流 - 提高性能
并行流可以利用多核处理器的优势,加快流操作的执行速度。可以通过 parallelStream 方法创建并行流:
import java.util.Arrays;
import java.util.List;
public class ParallelStreamExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.parallelStream()
.reduce(0, Integer::sum);
System.out.println(sum); // 输出: 15
}
}
组合多个 Stream
可以使用 flatMap 方法将多个 Stream 组合为一个:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamFlatMapExample {
public static void main(String[] args) {
List<List<Integer>> listOfLists = Arrays.asList(
Arrays.asList(1, 2, 3),
Arrays.asList(4, 5, 6),
Arrays.asList(7, 8, 9)
);
List<Integer> combinedList = listOfLists.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
System.out.println(combinedList); // 输出: [1, 2, 3, 4, 5, 6, 7, 8, 9]
}
}
错误处理
在流操作中,可能会出现异常,可以使用 peek 方法进行调试:
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public class StreamErrorHandlingExample {
public static void main(String[] args) {
List<String> items = Arrays.asList("1", "2", "a", "4");
List<Integer> numbers = items.stream()
.map(item -> {
try {
return Integer.parseInt(item);
} catch (NumberFormatException e) {
System.err.println("Error parsing: " + item);
return null;
}
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
System.out.println(numbers); // 输出: [1, 2, 4]
}
}
注意事项
- 流的惰性求值:中间操作是惰性求值的,只有终端操作执行时才会触发实际的计算。
- 不可重用:Stream 一旦使用完毕,不能再次使用。如果需要再次使用,需要重新生成 Stream。
- 并行流的使用:并行流在某些情况下可以显著提高性能,但并非所有场景都适用,应根据实际情况进行选择。
结语
本文详细介绍了 Java Stream API 的基本用法和一些实践技巧。通过合理使用 Stream API,可以使代码更加简洁、高效。希望读者能够通过本文的介绍,更好地掌握和应用 Stream API。