目录
1. 概述
Java Stream API 是 Java 8 及以上版本中提供的一种新特性,它支持对集合(Collections)进行声明式的操作。Stream API 可以用于执行复杂的数据转换操作,并支持并行处理。
2. Steam操作
中间操作(Intermediate operations)
中间操作返回的是一个新的 Stream
,可以继续进行链式调用。以下是一些常见的中间操作:
(1)filter
filter(Predicate<? super T> predicate)
: 过滤元素。
示例: 过滤出列表中所有偶数。
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]
(2)map
map(Function<? super T, ? extends R> mapper)
: 转换每个元素到对应的结果。
示例: 将每个字符串转换为大写。
List<String> words = Arrays.asList("hello", "world");
List<String> upperCaseWords = words.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(upperCaseWords); // 输出 [HELLO, WORLD]
(3)flatMap
flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)
: 将每个元素转换成另一个Stream
,然后将所有流连接成一个流。
示例:将包含单词列表的列表转换为单词流。
List<List<String>> listOfLists = Arrays.asList(
Arrays.asList("hello", "world"),
Arrays.asList("goodbye", "world")
);
List<String> words = listOfLists.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
System.out.println(words); // 输出 [hello, world, goodbye, world]
(4)sorted
sorted()
: 对元素进行自然排序。
示例:对整数列表进行自然排序。
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]
sorted(Comparator<? super T> comparator)
: 使用提供的比较器对元素进行排序。
示例:使用自定义比较器对字符串列表进行排序。
List<String> words = Arrays.asList("banana", "apple", "cherry");
List<String> sortedWords = words.stream()
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
System.out.println(sortedWords); // 输出 [cherry, banana, apple]
(5)peek
peek(Consumer<? super T> action)
: 用于调试,允许你无修改地查看流中的元素。
示例:打印每个元素,用于调试。
List<String> words = Arrays.asList("hello", "world");
List<String> upperCaseWords = words.stream()
.peek(System.out::println) // 打印每个元素
.map(String::toUpperCase)
.collect(Collectors.toList());
(6) limit
limit(long maxSize)
: 限制流的大小。
示例:限制流的大小。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
List<Integer> limitedNumbers = numbers.stream()
.limit(3)
.collect(Collectors.toList());
System.out.println(limitedNumbers); // 输出 [1, 2, 3]
(7) skip
skip(long n)
: 跳过流中的前 n 个元素。
示例:跳过列表中的前 n 个元素。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
List<Integer> skippedNumbers = numbers.stream()
.skip(3)
.collect(Collectors.toList());
System.out.println(skippedNumbers); // 输出 [4, 5, 6, 7, 8, 9]
(8)distinct
distinct()
: 去除重复元素。
示例:
List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 4, 4, 5, 5, 5, 6);
List<Integer> distinctNumbers = numbers.stream()
.distinct()
.collect(Collectors.toList());
System.out.println(distinctNumbers); // 输出 [1, 2, 3, 4, 5, 6]
终端操作(Terminal operations)
终端操作返回的是一个结果或一个副作用(例如打印结果),之后不能再对 Stream
进行操作。以下是一些常见的终端操作:
(1)forEach
forEach(Consumer<? super T> action)
: 对每个元素执行一个操作。
示例:对每个元素执行打印操作。
List<String> words = Arrays.asList("apple", "banana", "cherry");
// 预期结果:打印每个元素
words.stream().forEach(System.out::println);
// apple
// banana
// cherry
(2) collect
collect(Collector<? super T, A, R> collector)
: 将流转换为其他形式,如列表、集合或 Map。
示例:将流转换为列表。
List<String> words = Arrays.asList("apple", "banana", "cherry");
// 预期结果:将流转换为列表 [APPLE, BANANA, CHERRY]
List<String> upperCaseWords = words.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(upperCaseWords);
(3) reduce
reduce(T identity, BinaryOperator<T> accumulator)
: 通过一个起始值,反复利用 BinaryOperator 来处理和累积元素,返回一个值。
示例:使用 Integer::sum
作为累加器,将流中的整数相加。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 预期结果:计算总和 15
int sum = numbers.stream().reduce(0, Integer::sum);
System.out.println(sum); // 15
reduce(BinaryOperator<T> accumulator)
: 使用一个 BinaryOperator 来累积元素,返回一个 Optional。
示例: 使用 Integer::max
作为累加器,找到流中的最大值。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 预期结果:找到最大值 5
Optional<Integer> max = numbers.stream().reduce(Integer::max);
max.ifPresent(System.out::println); // 5
(4)toArray
toArray()
: 将流转换为对象数组。
示例:使用 toArray
方法将流转换为对象数组。
List<String> words = Arrays.asList("apple", "banana", "cherry");
// 预期结果:将流转换为对象数组 [apple, banana, cherry]
Object[] wordArray = words.stream().toArray();
System.out.println(Arrays.toString(wordArray));
toArray(IntFunction<A[]> generator)
: 将流转换为特定类型的数组。
示例:使用 toArray
方法将流转换为特定类型的数组。
List<String> words = Arrays.asList("apple", "banana", "cherry");
// 预期结果:将流转换为字符串数组 [apple, banana, cherry]
String[] wordArray = words.stream().toArray(String[]::new);
System.out.println(Arrays.toString(wordArray));
(5)min
min(Comparator<? super T> comparator)
: 找到最小元素。
示例:使用 min
方法找到流中的最小元素。
List<String> words = Arrays.asList("apple", "banana", "cherry");
// 预期结果:找到最小元素 apple
Optional<String> min = words.stream().min(String::compareTo);
min.ifPresent(System.out::println); // apple
(6)max
max(Comparator<? super T> comparator)
: 找到最大元素。
示例:使用 max
方法找到流中的最大元素。
List<String> words = Arrays.asList("apple", "banana", "cherry");
// 预期结果:找到最大元素 cherry
Optional<String> max = words.stream().max(String::compareTo);
max.ifPresent(System.out::println); // cherry
(7)count
count()
: 返回流中元素的数量。
示例:使用 count
方法返回流中元素的数量。
List<String> words = Arrays.asList("apple", "banana", "cherry");
// 预期结果:流中元素的数量 3
long count = words.stream().count();
System.out.println(count); // 3
(8)anyMatch
anyMatch(Predicate<? super T> predicate)
: 检查是否至少有一个元素匹配给定的谓词。
示例: 这个方法用于检查流中是否至少有一个元素匹配给定的谓词。
List<String> words = Arrays.asList("apple", "banana", "cherry");
// 预期结果:至少有一个元素以 "a" 开头
boolean anyMatch = words.stream().anyMatch(s -> s.startsWith("a"));
System.out.println(anyMatch); // 输出 true
(9)allMatch
allMatch(Predicate<? super T> predicate)
: 检查是否所有元素都匹配给定的谓词。
示例:这个方法用于检查流中是否所有元素都匹配给定的谓词。
List<String> words = Arrays.asList("apple", "banana", "cherry");
// 预期结果:所有元素都以 "c" 开头
boolean allMatch = words.stream().allMatch(s -> s.startsWith("c"));
System.out.println(allMatch); // 输出 false
(10)noneMatch
noneMatch(Predicate<? super T> predicate)
: 检查是否没有元素匹配给定的谓词。
示例: 这个方法用于检查流中是否没有元素匹配给定的谓词。
List<String> words = Arrays.asList("apple", "banana", "cherry");
// 预期结果:没有元素以 "z" 开头
boolean noneMatch = words.stream().noneMatch(s -> s.startsWith("z"));
System.out.println(noneMatch); // 输出 true
(11)findFirst
findFirst()
: 返回第一个元素。
示例:这个方法用于返回流中的第一个元素。
List<String> words = Arrays.asList("apple", "banana", "cherry");
// 预期结果:返回第一个元素 "apple"
Optional<String> first = words.stream().findFirst();
first.ifPresent(System.out::println); // apple
(12)findAny
findAny()
: 返回任意一个元素(在并行流中特别有用)。
示例:这个方法用于返回流中的任意一个元素。
List<String> words = Arrays.asList("apple", "banana", "cherry");
// 预期结果:返回任意一个元素,可能是 "apple", "banana" 或 "cherry"
Optional<String> any = words.stream().findAny();
any.ifPresent(System.out::println); // 输出一个随机的字符串元素
3. 创建 Stream
创建 Stream
的方式有很多:
(1) stream
- 通过集合的
stream()
方法。
示例:
List<String> list = Arrays.asList("apple", "banana", "cherry");
Stream<String> stream = list.stream();
(2) Arrays.stream
- 通过数组的
Arrays.stream(Object[])
方法。
示例:
String[] array = {"apple", "banana", "cherry"};
Stream<String> stream = Arrays.stream(array);
(3) Stream.of
- 使用
Stream.of(T... values)
静态方法。
示例:
Stream<String> stream = Stream.of("apple", "banana", "cherry");
(4) Stream.iterate
- 使用
Stream.iterate(T seed, UnaryOperator<T> f)
或Stream.generate(Supplier<T> s)
创建无限流。
// 使用 Stream.iterate 创建无限流
Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 1);
// 使用 Stream.generate 创建无限流
Stream<String> generateStream = Stream.generate(() -> "hello");
4. 并行 Streams
Stream
接口还有一个子接口 BaseStream
和一个并行流接口 ParallelStream
,用于并行处理数据。
并行流可以提高处理大量数据时的性能,因为它可以利用多核处理器的能力来并行地执行操作。但是,并不是所有的流操作都适合并行处理,你需要根据具体的情况来决定是否使用并行流
(1)parallelStream
-
parallelStream()
方法,该方法是Stream
接口的一个扩展方法。
示例:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
// 创建并行流
List<Integer> evenNumbers = numbers.parallelStream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println(evenNumbers); // 输出 [2, 4, 6, 8]
(2)parallel
parallel()
方法,该方法是BaseStream
接口的一个扩展方法。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
// 使用 parallel() 方法创建并行流
List<Integer> evenNumbers = numbers.stream()
.parallel() // 创建并行流
.filter(n -> n % 2 == 0) // 过滤偶数
.collect(Collectors.toList()); // 收集结果
System.out.println(evenNumbers); // 输出 [2, 4, 6, 8]
5. Collectors
Collectors
类提供了很多静态方法,用于实现常见的归约操作,如将元素累积到集合、字符串、Map 等。
(1)toList
-
toList()
: 将流中的元素收集到列表中。
示例:
List<String> words = Arrays.asList("apple", "banana", "cherry");
List<String> collectedWords = words.stream().collect(Collectors.toList());
System.out.println(collectedWords); // 输出 [apple, banana, cherry]
(2)toSet
toSet()
: 将流中的元素收集到集合中,自动去除重复项。
示例:
List<String> words = Arrays.asList("apple", "banana", "cherry", "apple", "banana");
Set<String> collectedWords = words.stream().collect(Collectors.toSet());
System.out.println(collectedWords); // 输出 [apple, banana, cherry]
(3)toMap
toMap()
: 将流中的元素收集到 Map 中,其中流中的元素作为键或值。
示例:
List<String> words = Arrays.asList("apple", "banana", "cherry");
Map<String, Integer> wordCount = words.stream().collect(Collectors.toMap(
word -> word,
word -> word.length()
));
System.out.println(wordCount); // 输出 {apple=5, banana=6, cherry=6}
(4)joining
joining()
: 将流中的元素连接成一个字符串。
示例:
List<String> words = Arrays.asList("apple", "banana", "cherry");
String joinedString = words.stream().collect(Collectors.joining("-"));
System.out.println(joinedString); // 输出 apple-banana-cherry
(5)counting
counting()
: 计算流中元素的数量。
示例:
List<String> words = Arrays.asList("apple", "banana", "cherry");
long wordCount = words.stream().collect(Collectors.counting());
System.out.println(wordCount); // 输出 3
6. 使用 Stream 的注意事项
- 流一旦执行终端操作,就会被消耗掉,不可以再使用该流执行其他操作。
- 流操作不会改变原始数据源,所有的操作都会返回一个新的
Stream
对象。 - 流操作可以并行化,但需要注意线程安全和数据竞争问题。