目录
2. 转换为字符串 (Collectors.joining())
3. 聚合操作 (Collectors.counting()、Collectors.summingInt())
1.单个参数的 reduce(BinaryOperator accumulator):
2.两个参数的 reduce(T identity, BinaryOperator accumulator):
3.三个参数的 reduce(U identity, BiFunction accumulator, BinaryOperator combiner):,>
Java Stream 是 Java 8 引入的一种新特性,用于处理集合中的数据流。Stream 提供了一种高效的方式来处理数据,包括过滤、映射、排序和聚合等操作。它可以通过函数式编程风格,简化对集合数据的操作。以下是对 Java Stream 的一些主要特性和使用方式的介绍:
1.主要特性
-
高效处理: Stream 支持并行处理,通过简单的 API 调用可以将操作并行化,提高性能。
-
不存储数据: Stream 不会存储数据,它只是对数据源的计算视图,操作不会修改原始数据源。
-
延迟计算: Stream 的大多数操作都是懒惰的,只有在最终操作(如
collect
,forEach
,reduce
)被调用时,才会执行所有的中间操作。 -
函数式编程: Stream 使用 Lambda 表达式,使得代码更简洁易读,符合函数式编程风格。
- 可消费性。stream只能被“消费”一次,一旦遍历过就会失效,就像容器的迭代器那样,想要再次遍历必须重新生成。
2.主要流程
创建Stream、中间操作、终止操作
1.创建Stream
a.从集合创建 Stream
最常见的方式是从集合(如 List
, Set
等)创建 Stream:
import java.util.Arrays;
import java.util.List;
public class StreamCreation {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 从集合创建 Stream
names.stream()
.forEach(System.out::println);
}
}
b.从数组创建 Stream
可以使用 Arrays.stream()
方法直接从数组创建 Stream:
import java.util.Arrays;
public class ArrayStream {
public static void main(String[] args) {
String[] namesArray = {"Alice", "Bob", "Charlie"};
// 从数组创建 Stream
Arrays.stream(namesArray)
.forEach(System.out::println);
}
}
c. 使用 Stream.of()
Stream.of()
方法可以直接从一组元素创建 Stream:
import java.util.stream.Stream;
public class StreamOf {
public static void main(String[] args) {
Stream<String> stream = Stream.of("Alice", "Bob", "Charlie");
stream.forEach(System.out::println);
}
}
d. 生成 Stream
可以使用 Stream.generate()
或 Stream.iterate()
生成 Stream:
import java.util.stream.Stream;
public class GenerateStream {
public static void main(String[] args) {
// 使用 Stream.generate()
Stream<Double> randomNumbers = Stream.generate(Math::random).limit(5);
randomNumbers.forEach(System.out::println);
// 使用 Stream.iterate()
Stream<Integer> evenNumbers = Stream.iterate(0, n -> n + 2).limit(5);
evenNumbers.forEach(System.out::println);
}
}
创建总结
Stream的创建和中间操作以及终止操作是独立的,是可以只创建不使用的,也不会有什么报错。
import java.util.Arrays;
import java.util.List;
public class StreamExample {
public static void main(String[] args) {
List<String> namesList = Arrays.asList("Alice", "Bob", "Charlie");
// 创建 Stream,但不调用任何方法
Stream<String> stream = namesList.stream();
// 这里没有中间操作和终止操作
System.out.println("Stream created, but not executed.");
}
}
2.中间操作
Stream
接口的部分常见方法:
操作类型 | 接口方法 |
---|---|
中间操作 | concat() distinct() filter() flatMap() limit() map() peek() skip() sorted() parallel() sequential() unordered() |
中间操作,都是创建出一个新的Stream流。
1. concat()
使用:
Stream<String> stream1 = Stream.of("A", "B", "C");
Stream<String> stream2 = Stream.of("D", "E");
Stream<String> result = Stream.concat(stream1, stream2);
result.forEach(System.out::println);
将两个流进行拼接
2. distinct()
使用:
Stream<String> stream = Stream.of("A", "B", "A", "C");
Stream<String> distinctStream = stream.distinct();
distinctStream.forEach(System.out::println);
distinct()函数是进行去重操作
3. filter()
使用:
Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5);
Stream<Integer> evenNumbers = numbers.filter(n -> n % 2 == 0);
evenNumbers.forEach(System.out::println);
注意事项: 过滤条件必须返回布尔值。
它接收一个 Predicate<T>
类型的参数,该参数是一个函数式接口,接受一个输入并返回一个布尔值。filter
通过这个条件来决定哪些元素应该被保留,哪些元素应该被排除。
Stream<T> filter(Predicate<? super T> predicate);
filter里面方法处理的数据类型要和stream流中元素的数据类型一致,? super T。
4. limit()
使用:
Stream<Integer> numbers = Stream.iterate(1, n -> n + 1);
Stream<Integer> limitedNumbers = numbers.limit(5);
limitedNumbers.forEach(System.out::println);
注意事项: limit()
不会影响原始流。
限制元素数量: limit(long maxSize)
会返回一个新流,该流包含最多 maxSize
个元素。
5. map()
使用:
Stream<String> stream = Stream.of("A", "B", "C");
Stream<Integer> lengths = stream.map(String::length);
lengths.forEach(System.out::println);
注意事项: 映射函数应该适用于流中的每个元素。
将流中的每个元素转换为另外一种形式。它接收一个函数作为参数,将该函数应用到流中的每个元素,并将其结果收集到一个新的流中。
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
表示一个输入参数 T
,返回值为 R
的函数。它将流中的每个元素从类型 T
转换为类型 R
。
返回值: 返回一个新流,该流中的每个元素都是通过将映射函数应用到原始流的每个元素后得到的。
import java.util.List;
import java.util.stream.Collectors;
class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
}
public class MapExample {
public static void main(String[] args) {
List<Person> people = List.of(
new Person("Alice", 30),
new Person("Bob", 25),
new Person("Charlie", 35)
);
// 使用 map() 提取每个人的名字
List<String> names = people.stream()
.map(Person::getName) // 映射到每个 Person 的名字
.collect(Collectors.toList()); // 收集到列表中
names.forEach(System.out::println);
}
}
import java.util.stream.Stream;
public class MapExample {
public static void main(String[] args) {
Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5);
numbers.map(n -> n * n) // 将每个数字映射为其平方
.forEach(System.out::println); // 打印结果
}
}
可以看到,map()是做数据转换的,这个转换即可以是数据类型,也可以是数据的值的变化,如一个List<对象1> 可以转化为List<对象2> 也可以改变用来改变对象中的值。只要构建合适的方法放到map中就可以。
6. peek()
使用:
Stream<Integer> numbers = Stream.of(1, 2, 3, 4);
numbers.peek(n -> System.out.println("Processing: " + n))
.map(n -> n * 2)
.forEach(System.out::println);
注意事项: 不要在peek()
中进行状态更改,因为它可能会导致不可预测的结果。
用于对流中的每个元素执行一些操作,但不会改变元素本身。它通常用于调试,帮助查看流在处理过程中的状态。
Stream<T> peek(Consumer<? super T> action);
-
Consumer<? super T>
: 这是一个函数式接口,表示一个接受单个参数且没有返回值的操作。在peek()
中,它会对流中的每个元素执行这个操作,但不会影响流本身。 -
返回值: 返回一个与原始流相同的流(即流中的元素保持不变)。
- 辅助调试:
peek()
的主要用途是调试,帮助开发者在流操作中间查看流的内容或检查元素处理的过程。
import java.util.stream.Stream;
public class PeekExample {
public static void main(String[] args) {
Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5);
numbers.peek(n -> System.out.println("Before filter: " + n)) // 查看原始流的元素
.filter(n -> n % 2 == 0) // 筛选出偶数
.peek(n -> System.out.println("After filter: " + n)) // 查看过滤后的元素
.forEach(System.out::println); // 打印最终的结果
}
}
Before filter: 1
Before filter: 2
Before filter: 3
Before filter: 4
Before filter: 5
After filter: 2
2
After filter: 4
4
也可以用来进行日志的记录。
import java.util.logging.Logger;
import java.util.stream.Stream;
public class PeekLoggingExample {
private static final Logger logger = Logger.getLogger(PeekLoggingExample.class.getName());
public static void main(String[] args) {
Stream<String> names = Stream.of("Alice", "Bob", "Charlie");
names.peek(name -> logger.info("Processing: " + name)) // 日志记录
.map(String::toUpperCase) // 转换为大写
.forEach(System.out::println); // 打印最终结果
}
}
7. skip()
使用:
Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5);
Stream<Integer> skippedNumbers = numbers.skip(2);
skippedNumbers.forEach(System.out::println);
注意事项: skip()
会忽略前面的元素,因此流的元素数量可能会减少。
跳过流中的前 n
个元素。它允许忽略流的前部分元素,并返回一个不包含这些元素的新流。
Stream<T> skip(long n);
可以与limit()组合使用实现分页的效果
import java.util.stream.Stream;
public class SkipAndLimitExample {
public static void main(String[] args) {
Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9);
numbers.skip(5) // 跳过前 5 个元素
.limit(3) // 只获取接下来的 3 个元素
.forEach(System.out::println); // 打印结果
}
}
6
7
8
如果使用 skip()
跳过的元素数目大于流的总元素数,返回的将是一个空流。
8. sorted()
使用:
Stream<Integer> numbers = Stream.of(5, 3, 1, 4, 2);
Stream<Integer> sortedNumbers = numbers.sorted();
sortedNumbers.forEach(System.out::println);
注意事项: 默认按照自然顺序排序,可以传入比较器进行自定义排序。
当流中的元素是数字、字符串或实现了 Comparable
接口的对象时,自动按照升序排序。
也可以自定义比较器
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
class Person {
String name;
int age;
double salary;
Person(String name, int age, double salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
@Override
public String toString() {
return name + " (" + age + " years old, $" + salary + ")";
}
}
public class CustomComparatorExample {
public static void main(String[] args) {
List<Person> people = Arrays.asList(
new Person("John", 25, 5000),
new Person("Alice", 30, 7000),
new Person("Bob", 20, 5000),
new Person("Charlie", 25, 7000),
new Person("Dave", 30, 5000)
);
// 自定义复杂比较器:先按工资降序,工资相同按年龄升序,年龄相同按名字字典顺序
Comparator<Person> complexComparator = Comparator
.comparingDouble((Person p) -> p.salary).reversed() // 按 salary 降序
.thenComparingInt(p -> p.age) // 如果 salary 相同,按 age 升序
.thenComparing(p -> p.name); // 如果 age 也相同,按 name 升序
// 使用复杂比较器对列表进行排序
people.stream()
.sorted(complexComparator)
.forEach(System.out::println); // 打印排序后的结果
}
}
3.终结操作
因为Stream 中的中间操作是懒加载的,只有在调用终结操作时,中间操作才会执行。中间操作会返回一个新的流,而终结操作会触发流的执行并产生最终的结果。
常见的终结操作
1. forEach()
forEach()
是最常见的终结操作之一。它对流中的每个元素执行指定的操作,但不会返回任何结果。
forEach()
接受一个 Consumer
接口的实例作为参数(只接受一个入参且无返回值的方法都可以),这意味着你可以传递一个 lambda 表达式或方法引用。它对流的每个元素执行这个操作,遍历的顺序取决于流的类型(顺序流或并行流)。forEach()
常用于执行有副作用的操作,比如修改外部状态。然而,forEach()
并不保证对每个元素的操作是线程安全的,因此在并行流中应谨慎使用。
List<String> fruits = Arrays.asList("apple", "banana", "orange");
StringBuilder result = new StringBuilder();
// forEach 遍历每个水果,并在末尾追加一个空格
fruits.stream().forEach(fruit -> result.append(fruit).append(" "));
System.out.println(result.toString()); // 输出: "apple banana orange "
在顺序流中,forEach()
按照流中元素的顺序依次处理每个元素;而在并行流中,forEach()
可能不会保证顺序,这使得并行处理更快但不可预测元素的处理顺序。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.stream().forEach(System.out::println); // 输出 1 2 3 4 5
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.parallelStream().forEach(System.out::println); // 输出顺序不定
如果你在并行流中仍然需要保证顺序,可以使用 forEachOrdered()
。它保证无论流是顺序还是并行的,操作都会按照流中元素的顺序进行。
2.collect()
广泛用于将流中的元素汇聚成某种结果,通常是集合、字符串或一些复合数据结构。它是 Stream
中最强大和灵活的操作之一,可以根据不同的需求,灵活地将流的结果转换为集合、映射、统计信息等。
collect()
方法需要一个 Collector
来定义如何处理流中的元素。Collector
是一个带有一组预定义策略的接口,负责将流的元素聚合起来。Java 标准库提供了 Collectors
工具类,它包含了许多常用的 Collector
实现。
collect()
方法的常见签名:
<R, A> R collect(Collector<? super T, A, R> collector);
其中:
T
是流中元素的类型。A
是累加器的类型,表示中间结果的类型。R
是收集操作的结果类型,即最终结果。
常见的 collect()
用法
1. 收集为 List
、Set
或 Map
Collectors.toList()
、Collectors.toSet()
和 Collectors.toMap()
是最常见的收集操作,它们分别将流中的元素收集为 List
、Set
或 Map
。
//收集为List
List<String> fruits = Arrays.asList("apple", "banana", "orange");
List<String> fruitList = fruits.stream().collect(Collectors.toList());
System.out.println(fruitList); // 输出: [apple, banana, orange]
//收集为set
Set<String> fruitSet = fruits.stream().collect(Collectors.toSet());
System.out.println(fruitSet); // 输出: [banana, apple, orange],无序的
//收集为map
Map<Integer, String> fruitMap = fruits.stream()
.collect(Collectors.toMap(String::length, fruit -> fruit, (existing, replacement) -> existing));
System.out.println(fruitMap); // 输出: {5=apple, 6=orange, 6=banana}(key 为水果名长度)
2. 转换为字符串 (Collectors.joining()
)
Collectors.joining()
可以将流中的元素拼接成一个字符串,支持指定分隔符、前缀和后缀。
String resultWithWrapper = fruits.stream()
.collect(Collectors.joining(", ", "[", "]"));
System.out.println(resultWithWrapper); // 输出: "[apple, banana, orange]"
3. 聚合操作 (Collectors.counting()
、Collectors.summingInt()
)
统计数量 (Collectors.counting()
)、求和操作 (Collectors.summingInt()
)
long count = fruits.stream().collect(Collectors.counting());
System.out.println(count); // 输出: 3
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream().collect(Collectors.summingInt(Integer::intValue));
System.out.println(sum); // 输出: 15
4. 分组与分区
Collectors.groupingBy()
和 Collectors.partitioningBy()
是进行数据分组和分区的强大工具。
按某个属性分组 (groupingBy()
):
Map<Integer, List<String>> groupedByLength = fruits.stream()
.collect(Collectors.groupingBy(String::length));
System.out.println(groupedByLength); // 输出: {5=[apple], 6=[banana, orange]}
groupingBy(Function<? super T, ? extends K> classifier)
这个重载版本是最简单和最常用的形式,它将根据分类器 (classifier
) 对流中的元素进行分组,返回 Map<K, List<T>>
,其中 K
是分组的键,List<T>
是该组中的元素列表。
List<String> fruits = Arrays.asList("apple", "banana", "orange", "pear", "kiwi");
// 按照字符串长度进行分组
Map<Integer, List<String>> groupedByLength = fruits.stream()
.collect(Collectors.groupingBy(String::length));
System.out.println(groupedByLength);
// 输出: {5=[apple, peach], 6=[banana, orange], 4=[kiwi]}
groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream)
这个版本允许你指定下游收集器 (downstream
),对每个分组内的元素进一步处理。例如,你可以将分组后的元素列表转换为 Set
,或统计每组的元素数量。
List<String> fruits = Arrays.asList("apple", "banana", "orange", "pear", "kiwi");
// 按字符串长度分组,并将每组的元素存入 Set 中
Map<Integer, Set<String>> groupedByLengthAsSet = fruits.stream()
.collect(Collectors.groupingBy(String::length, Collectors.toSet()));
System.out.println(groupedByLengthAsSet);
// 输出: {5=[apple, peach], 6=[banana, orange], 4=[kiwi]}
groupingBy(Function<? super T, ? extends K> classifier, Supplier<M> mapFactory, Collector<? super T, A, D> downstream)
这个重载形式允许你自定义分组使用的 Map
实现,并对每个分组的元素应用下游收集器。通常用于需要指定自定义 Map
类型时,比如 TreeMap
、ConcurrentHashMap
等。
List<String> fruits = Arrays.asList("apple", "banana", "orange", "pear", "kiwi");
// 使用 TreeMap 按照字符串长度分组,并统计每组的元素数量
Map<Integer, Long> groupedByLengthAndCount = fruits.stream()
.collect(Collectors.groupingBy(String::length, TreeMap::new, Collectors.counting()));
System.out.println(groupedByLengthAndCount);
// 输出: {4=1, 5=2, 6=2}
-
classifier
(分类器):- 这是一个
Function<T, K>
类型的函数,用于从流中的每个元素提取分组的键。可以是方法引用(如String::length
)或 lambda 表达式(如e -> e.getCategory()
)。 - 它定义了如何将元素分类。
- 这是一个
-
downstream
(下游收集器):- 这是一个
Collector
,用于处理分组后的结果。默认情况下,每个分组内的元素会被收集到一个List
中。如果你想对每个分组内的元素进行进一步操作,比如转为Set
或统计数量,使用这个参数可以实现。 - 常见的下游收集器包括
Collectors.toSet()
、Collectors.counting()
、Collectors.mapping()
等。
- 这是一个
-
mapFactory
(映射工厂):- 用于指定结果的
Map
类型。默认情况下,groupingBy()
返回的Map
是HashMap
,但你可以通过传递一个Supplier<M>
来创建其他类型的Map
,如TreeMap
或ConcurrentHashMap
。
- 用于指定结果的
按条件分区 (partitioningBy()
):
Map<Boolean, List<String>> partitioned = fruits.stream()
.collect(Collectors.partitioningBy(fruit -> fruit.length() > 5));
System.out.println(partitioned); // 输出: {false=[apple], true=[banana, orange]}
用于将流中的元素分成两个部分,基于给定的谓词(Predicate
)。它的返回值是一个 Map<Boolean, List<T>>
,其中:
true
对应满足谓词的元素列表。false
对应不满足谓词的元素列表。
partitioningBy()
还可以与其他收集器结合使用。例如,你可以在分区后的列表上进行额外的操作:
import java.util.*;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<String> words = Arrays.asList("apple", "banana", "kiwi", "apricot", "blueberry");
// 使用 partitioningBy 进行分组并统计每组的元素数量
Map<Boolean, Long> partitionedCount = words.stream()
.collect(Collectors.partitioningBy(
word -> word.startsWith("a", 0),
Collectors.counting()
));
// 输出结果
System.out.println("Count of words starting with 'a': " + partitionedCount.get(true)); // 以 'a' 开头的单词数量
System.out.println("Count of words not starting with 'a': " + partitionedCount.get(false)); // 不以 'a' 开头的单词数量
}
}
5. 自定义 Collector
如果内置的 Collectors
无法满足需求,你也可以自己实现 Collector
接口。自定义 Collector
可以让你完全控制收集过程。
例如,创建一个 Collector
来手动拼接字符串:
Collector<String, StringBuilder, String> customCollector = Collector.of(
StringBuilder::new, // Supplier
StringBuilder::append, // Accumulator
StringBuilder::append, // Combiner
StringBuilder::toString // Finisher
);
String customResult = fruits.stream().collect(customCollector);
System.out.println(customResult); // 输出: "applebananaorange"
- Supplier:创建一个用于收集结果的容器(比如
ArrayList
或StringBuilder
)。 - Accumulator:定义如何将流中的元素添加到容器中。
- Combiner:在并行流中,定义如何将多个部分结果合并到一起。
- Finisher:将收集器中的结果转换为最终的返回值(有时可省略,如
toList()
)。 - Characteristics:定义收集器的特性(例如,是否可以并行操作)。
3.reduce()
用于将流中的元素组合成一个单一的结果。它通过反复应用一个二元操作(BinaryOperator
)将流的元素进行合并,最终生成一个结果。reduce()
通常用于聚合操作,例如求和、求积、拼接字符串等。
BinaryOperator<T>
继承自 BiFunction<T, T, T>
,表示一种二元操作,它接受两个相同类型的参数,并返回与参数类型相同的结果。这个接口通常用于执行像加法、乘法、比较等二元运算。
BinaryOperator<T>
是 BiFunction<T, T, T>
的子接口,区别在于:
BiFunction<T, U, R>
:接受两个不同类型的参数T
和U
,并返回第三种类型R
的结果。BinaryOperator<T>
:接受两个相同类型的参数T
,并返回相同类型T
的结果。
@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T, T, T> {
// 静态方法
static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {
return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
}
static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {
return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
}
}
//继承自BiFunction的方法
T apply(T t1, T t2);
静态方法maxBy和minBy返回的类型都是BinaryOperator<T> ,是一个实例化后的对象,这个对象的方法apply被重写了,如maxBy返回的对象就是(a, b) -> comparator.compare(a, b) >= 0 ? a : b
等价于 BinaryOperator<T> w=(a, b) -> comparator.compare(a, b) >= 0 ? a : b, apply方法被重写为:comparator.compare(a, b) >= 0 ? a : b
用该接口比较大小
import java.util.Comparator;
import java.util.function.BinaryOperator;
public class Main {
public static void main(String[] args) {
// Comparator 比较两个整数的大小
Comparator<Integer> comparator = Integer::compare;
// 使用 maxBy 返回两个数中较大的那个
BinaryOperator<Integer> maxOperator = BinaryOperator.maxBy(comparator);
Integer maxResult = maxOperator.apply(3, 7);
System.out.println("Max: " + maxResult); // 输出: Max: 7
// 使用 minBy 返回两个数中较小的那个
BinaryOperator<Integer> minOperator = BinaryOperator.minBy(comparator);
Integer minResult = minOperator.apply(3, 7);
System.out.println("Min: " + minResult); // 输出: Min: 3
}
}
reduce()
的三种常见形式
1.单个参数的 reduce(BinaryOperator<T> accumulator)
:
这个版本没有初始值,它会对流中的元素应用累加器,并返回一个 Optional<T>
,因为流可能为空。
import java.util.*;
import java.util.stream.*;
public class Main {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 使用 reduce 求和
Optional<Integer> sum = numbers.stream().reduce((a, b) -> a + b);
// 输出结果
sum.ifPresent(result -> System.out.println("Sum: " + result)); // 输出: Sum: 15
}
}
2.两个参数的 reduce(T identity, BinaryOperator<T> accumulator)
:
这个版本有一个初始值(identity
),即使流为空,也会返回该初始值。
import java.util.*;
import java.util.stream.*;
public class Main {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 使用 reduce 求和,初始值为 0
Integer sum = numbers.stream().reduce(0, (a, b) -> a + b);
// 输出结果
System.out.println("Sum: " + sum); // 输出: Sum: 15
}
}
import java.util.*;
import java.util.stream.*;
public class Main {
public static void main(String[] args) {
List<String> words = Arrays.asList("apple", "banana", "cherry");
// 使用 reduce 拼接字符串
String result = words.stream().reduce("", (a, b) -> a + b + " ");
// 输出结果
System.out.println(result.trim()); // 输出: apple banana cherry
}
}
待用初始值
3.三个参数的 reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner)
:
这个版本用于并行流处理,允许将结果拆分、分别计算,再合并(适用于并行流)。
import java.util.*;
import java.util.stream.*;
public class Main {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 使用并行流和 reduce 计算乘积
Integer product = numbers.parallelStream().reduce(1, (a, b) -> a * b, (a, b) -> a * b);
// 输出结果
System.out.println("Product: " + product); // 输出: Product: 3628800
}
}
总结
reduce()
的核心 是它的累加操作,它不断地将流中的元素与之前的结果进行累加,最终返回单个结果。- 它支持灵活的初始值和二元操作,适用于很多场景,如求和、求积、字符串拼接等。
reduce()
还可以用于并行流,通过combiner
合并多个部分的计算结果。