简介
Java 8 引入的 Stream API 是 Java 平台的一个重大改进,它提供了一种高效且表达力强的方式来处理数据集合(如 List、Set)。Stream API 可以让你以声明方式处理数据集合(专注于你要做什么,而不是怎么做),并且可以利用多核处理器的优势进行并行计算。Stream API 是 Java 8 引入的一个功能强大的工具,它提供了一种简洁而高效的方式来处理集合数据。通过使用 Stream,我们可以对集合进行过滤、映射、排序、聚合等操作,而无需编写繁琐的循环和条件语句。Stream 的操作可以串行执行,也可以并行执行,从而提高处理大量数据的效率。
主要特性和优点
- 函数式编程支持:Stream API 充分利用了 Java 8 的 Lambda 表达式和函数式编程概念。
- 声明式数据处理:通过 Stream API,你可以以接近自然语言的方式表达数据处理逻辑,代码更加清晰、简洁。
- 并行处理能力:Stream API 提供了自动的并行处理能力,可以简化并行编程的复杂性,可以自动利用多核处理器,提高处理大量数据的效率。
- 延迟执行:Stream API 的操作是延迟执行的,这意味着它们不会立即执行,而是等到需要结果时才执行。
- 无副作用:Stream API 的操作应该是无副作用的,即它们不会修改数据源。
- 易于集成和扩展:Stream API 可以与 Java 集合框架无缝集成,并且可以很容易地通过自定义的 Spliterator 进行扩展。
基本操作
Stream API 中的操作分为两类:中间操作(Intermediate Operations)和终端操作(Terminal Operations)。
1、中间操作:返回 Stream 本身,因此可以链式调用。中间操作是“惰性”的,即在调用终端操作之前,它们不会执行任何处理。常见的中间操作包括 filter()
, map()
, sorted()
, limit()
, skip()
等。
中间操作包括有状态和无状态操作两种:
-
有状态操作:操作需要维护状态来正确执行,每个元素的处理可能依赖于其他元素的状态或上下文。
例如,sorted和distinct操作,是需要维护一个状态,一个是记录位置,一个是记录是否出现过。 -
无状态操作:每个元素的处理都是独立的,不依赖于其他元素的状态。
例如,filter、map和flatMap,只是根据输入元素生成输出元素,而不会受到其他元素的影响。
操作类型 | 操作方法 | 描述 |
---|---|---|
中间操作 | filter(Predicate<? super T> predicate) | 过滤流中的元素,只保留满足谓词条件的元素 |
map(Function<? super T, ? extends R> mapper) | 将流中的每个元素映射成另一种形式 | |
flatMap(Function<? super T, ? extends Stream<? extends R>> mapper) | 将流中的每个元素都转换成另一个流,然后将所有流连接成一个流 | |
distinct() | 去除流中的重复元素(基于元素的 equals() 和 hashCode() 方法) | |
sorted() | 对流中的元素进行自然排序(需要元素实现了 Comparable 接口) | |
sorted(Comparator<? super T> comparator) | 使用自定义的比较器对流中的元素进行排序 | |
peek(Consumer<? super T> action) | 对流中的每个元素执行操作,但不影响流本身,主要用于调试 | |
limit(long maxSize) | 限制流的元素个数 | |
skip(long n) | 跳过流的前n个元素 |
2、终端操作:返回一个结果或副作用,例如执行一个操作或返回一个非 Stream 的值。终端操作会触发 Stream 管道中所有中间操作的执行。常见的终端操作包括 forEach()
, collect()
, reduce()
, findFirst()
, min()
, max()
等。
终止操作包括短路操作和非短路操作两种:
- 短路操作:处理元素时,满足某个条件就立即返回结果,无需处理所有元素。
例如,findFirst、findAny、anyMatch和allMatch - 非短路操作:指必须处理所有元素才能得到最终结果;
例如,forEach、reduce和collect
操作类型 | 操作方法 | 描述 |
---|---|---|
终止操作 | forEach(Consumer<? super T> action) | 遍历流中的每个元素并执行操作 |
toArray() | 将流中的元素收集到一个数组中(注意:这通常需要一个显式的类型参数,如 toArray(String[]::new) ) | |
reduce(BinaryOperator<T> accumulator) | 归约操作,将流中的元素组合起来,得到一个值(需要元素之间有明确的组合方式) | |
reduce(T identity, BinaryOperator<T> accumulator) | 带初始值的归约操作 | |
collect(Collector<? super T, A, R> collector) | 收集操作,将流中的元素收集到一个集合中,通常与 Collectors 类一起使用 | |
min(Comparator<? super T> comparator) | 找出流中的最小元素(根据提供的比较器) | |
max(Comparator<? super T> comparator) | 找出流中的最大元素(根据提供的比较器) | |
count() | 计算流中元素的个数 | |
anyMatch(Predicate<? super T> predicate) | 检查流中是否存在至少一个元素满足条件 | |
allMatch(Predicate<? super T> predicate) | 检查流中的所有元素是否都满足条件 | |
noneMatch(Predicate<? super T> predicate) | 检查流中是否不存在任何元素满足条件 | |
findFirst() | 查找流中的第一个元素(返回一个包含单个元素的 Optional ) | |
findAny() | 查找流中的任意一个元素(在并行流中可能不返回第一个元素) |
官网: Stream()官网
一、操作
1、获取流
1.1 从集合(Collection)中获取
@Test
void list() {
// 创建一个List集合
List<String> names = Arrays.asList("John", "Alice", "Bob", "David");
// 获取顺序流
Stream<String> stream = names.stream();
// 使用流进行操作(这里只是示例,没有实际的操作)
stream.forEach(System.out::println);
// 获取并行流
Stream<String> parallelStream = names.parallelStream();
// 使用并行流进行操作(这里只是示例,没有实际的操作)
parallelStream.forEach(System.out::println);
Set<Integer> set = new HashSet<Integer>() {{
add(1);
add(2);
add(3);
}};
// 通过Set获取
Stream<Integer> stream2 = set.stream();
stream2.forEach(System.out::println);
Map<String, String> map = new HashMap<>();
map.put("map1","test1");
map.put("map2","test2");
// 通过Map.entrySet获取
Stream<Map.Entry<String, String>> stream3 = map.entrySet().stream();
stream3.forEach(System.out::println);
// 通过Map.keySet获取
Stream<String> stream4 = map.keySet().stream();
stream4.forEach(System.out::println);
// 通过Map.values获取
Stream<String> stream5 = map.values().stream();
stream5.forEach(System.out::println);
}
1.2 从数组中获取
@Test
void array(){
// 对象数组
String[] strings = {"Hello", "World", "Java", "Stream"};
Stream<String> stringStream = Arrays.stream(strings);
// 基本类型数组(以int为例)
int[] numbers = {1, 2, 3, 4, 5};
Stream<Integer> intStream = Arrays.stream(numbers).boxed(); // 基本类型流需要装箱
// 使用流进行操作(这里只是示例,没有实际的操作)
stringStream.forEach(System.out::println);
intStream.forEach(System.out::println);
}
1.3 从其他数据源获取流
@Test
void stream(){
// 使用Stream.of()从多个元素中创建流
Stream<String> stringStream = Stream.of("Apple", "Banana", "Cherry");
// 使用Stream.iterate()创建无限流(这里需要提供一个终止条件来限制流的大小)
Stream<Integer> infiniteIntStream = Stream.iterate(1, n -> n + 1).limit(5); // 生成1到5的流
// 使用Stream.generate()创建无限流(基于提供的Supplier)
Stream<Double> randomStream = Stream.generate(Math::random).limit(10); // 生成10个随机数
stringStream.forEach(System.out::println);
infiniteIntStream.forEach(System.out::println);
randomStream.forEach(System.out::println);
}
1.4 从文件中创建流
@Test
void file(){
// Paths.get("example.txt") 为当前工作目录下名为 example.txt 的文件,没有创建会报错,也可以写绝对路径
try (Stream<String> stream = Files.lines(Paths.get("src/test/java/com/example/demo/example.txt"))) {
// 读取文件每一行并打印
stream.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
}
2、中间操作
2.1 map():
-
将流中的每个元素映射到另一个元素。例如,将字符串转换为大写:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie"); List<String> upperCaseNames = names.stream() .map(String::toUpperCase) .collect(Collectors.toList()); upperCaseNames.forEach(System.out::println);
2.2 filter():
-
过滤流中的元素,只保留满足特定条件的元素。例如,过滤出长度大于3的字符串:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie"); List<String> longNames = names.stream() .filter(name -> name.length() > 3) .collect(Collectors.toList()); longNames.forEach(System.out::println);
2.3 flatMap():
-
将流中的每个元素转换为一个流,并将所有流合并成一个流。例如,将每个字符串拆分为字符并将字符流合并:
List<String> words = Arrays.asList("hello", "world"); List<Character> characters = words.stream() .flatMap(word -> word.chars().mapToObj(c -> (char) c)) .collect(Collectors.toList()); characters.forEach(System.out::println);
2.4 distinct():
-
去除流中的重复元素:
List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 4, 4); List<Integer> distinctNumbers = numbers.stream() .distinct() .collect(Collectors.toList()); distinctNumbers.forEach(System.out::println);
2.5 sorted():
.sorted(Comparator.reverseOrder())
倒叙排序
Comparator.comparing(Item::getName).reversed()
根据字段名倒叙排序
-
对流中的元素进行排序。例如,按自然顺序排序:
List<Integer> numbers = Arrays.asList(1, 2, 6, 3, 5, 4); List<Integer> sortedNames = numbers.stream() .sorted() // 正序排序 .collect(Collectors.toList()); sortedNames.forEach(System.out::println);
2.6 limit():
-
限制流中元素的数量。例如,取前3个元素:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "Dave"); List<String> firstThreeNames = names.stream() .limit(3) .collect(Collectors.toList()); firstThreeNames.forEach(System.out::println);
2.7 skip():
-
跳过流中的前N个元素。例如,跳过前2个元素:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "Dave"); List<String> skippedNames = names.stream() .skip(2) .collect(Collectors.toList()); skippedNames.forEach(System.out::println);
2.8 peek():
-
对流中的每个元素执行一个操作,但不改变流本身。主要用于调试:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie"); names.stream() .peek(name -> System.out.println("Processing: " + name)) .map(String::toUpperCase) .collect(Collectors.toList()); names.forEach(System.out::println);
2.9 mapToDouble(), mapToInt(), mapToLong():
-
将元素映射到特定原始类型的流:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie"); IntStream intStream = names.stream().mapToInt(String::length); intStream.forEach(System.out::println);
这些数值流(IntStream
、LongStream
、DoubleStream
)提供了专门的数值聚合操作(如 sum
、average
、max
、min
等),这些操作比通用的 Stream<T>
提供的操作更高效,因为它们可以直接在数值类型上操作,而无需进行装箱(boxing)和拆箱(unboxing)操作。
3、终止操作
3.1 forEach():
- 对流中的每个元素执行给定的操作。
语法:forEach(Consumer<? super T> action)
Consumer
: 这是一个函数式接口,接受一个参数并返回void
。它代表了一个对单个元素执行的操作,例如打印元素、更新外部变量等。<? super T>
: 这是 Java 泛型中的一个通配符用法,表示Consumer
接口可以接受任何T
类型或T
的任何超类型的实例。这使得forEach
方法能够应用于任何类型的流。
List<String> items = Arrays.asList("A", "B", "C");
items.stream().forEach(System.out::println); // 打印每个元素
3.2 count():
- 返回流中元素的数量
List<String> items = Arrays.asList("A", "B", "C");
long count = items.stream().count(); // 计算元素数量
System.out.println(count);
3.3 reduce():
- 使用给定的二元操作符将流中的元素归约到一个值。
语法:reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator<U> combiner)
identity
:一个初始值,它是累积操作的起始值。对于某些类型的操作(如求和、求积等),这个初始值可以是0或1。accumulator
:一个二元操作函数,它接受两个参数:一个是累积结果,另一个是集合中的当前元素。这个函数将这两个参数结合起来,生成一个新的累积结果。combiner
:一个可选的二元操作符,用于并行处理时合并各个线程的结果。如果不提供这个参数,那么在并行处理时会使用accumulator
作为合并操作。
OptionalInt reduced = numbers.stream().mapToInt(Integer::intValue).reduce((a, b) -> a + b);
reduced.ifPresent(System.out::println);
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
int sum = numbers.stream().reduce(0, Integer::max);
System.out.println("最大值: " + sum);
List<String> words = Arrays.asList("Java", "Stream", "API");
String result = words.parallelStream()
.reduce("",
(partialString, word) -> partialString + word,
(left, right) -> left + right);
System.out.println("Result: " + result);
3.4 collect()
- 将流中的元素汇总到一个集合或执行其他汇总操作。
语法:collect(Collector<? super T, A, R> collector)
T
:流中元素的类型。A
:累加器的类型,用于累积流中的元素。R
:结果的类型,即收集操作的最终输出。
1. 归集: toList(),toSet(),toMap()
- 因为流不存储数据,那么在流中的数据完成处理后,需要将流中的数据重新归集到新的集合里。 toList、toSet和toMap比较常用,另外还有toCollection、toConcurrentMap等复杂一些的用法。
List<String> items = Arrays.asList("A", "B", "C", "C");
Set<String> set = items.stream().collect(Collectors.toSet()); // 收集到Set
System.out.println(set);
List<String> list = items.stream().collect(Collectors.toList()); // 收集到List
System.out.println(list);
Map<String, Integer> map = items.stream().collect(Collectors.toMap(
String::toUpperCase, // 键
String::length, // 值
// (oldValue, newValue) -> oldValue + newValue, // 合并函数(处理键值重复问题,将值进行合并)
(existing, replacement) -> existing // 选择保留现有的值
));// 收集到map
System.out.println(map);
2. 计数: counting()
- 统计流中元素的数量。
List<String> items = Arrays.asList("apple", "banana", "cherry");
long count = items.stream().collect(Collectors.counting());
System.out.println(count);
3. 平均值: averagingInt(), averagingLong(), averagingDouble()
-
计算流中元素数值的平均值。
语法:
Collectors.averagingInt(ToIntFunction<T> mapper)
Collectors.averagingLong(ToLongFunction<T> mapper)
Collectors.averagingDouble(ToDoubleFunction<T> mapper)
ToDoubleFunction<T> mapper
:这是一个函数式接口的参数,它指定了一个转换函数。这个转换函数接受流中元素的类型T
作为输入,并返回一个double
类型的值作为输出。这个double
值随后会被用于计算。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
double averagingDouble = numbers.stream().collect(Collectors.averagingDouble(n -> n));
double averagingInt = numbers.stream().collect(Collectors.averagingInt(n -> n));
double averagingLong = numbers.stream().collect(Collectors.averagingLong(n -> n));
System.out.println(averagingDouble);
System.out.println(averagingInt);
System.out.println(averagingLong);
4. 求和: summingInt(), summingLong(), summingDouble()
-
对流中元素的数值属性进行求和。
语法:
Collectors.summingInt(ToIntFunction<? super T> mapper)
Collectors.summingLong(ToIntFunction<? super T> mapper)
Collectors.summingDouble(ToIntFunction<? super T> mapper)
ToIntFunction<? super T> mapper
:用于从类型为 T
或其父类型的流元素中提取出一个 int
类型的数值。? super T
表示该函数可以接受 T
类型或者 T
的任何父类型的对象作为输入参数 。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Integer summingInt = numbers.stream().collect(Collectors.summingInt(n -> n));
Long summingLong = numbers.stream().collect(Collectors.summingLong(n -> n));
Double summingDouble = numbers.stream().collect(Collectors.summingDouble(n -> n));
System.out.println(summingInt);
System.out.println(summingLong);
System.out.println(summingDouble);
5. 最值:maxBy(), minBy()
-
找出流中元素的最大值或最小值。
语法:
-
Collectors.maxBy(Comparator<? super T> comparator)
-
Collectors.minBy(Comparator<? super T> comparator)
Comparator<? super T> comparator
:这是一个函数式接口Comparator
的实例,它定义了如何比较两个类型为T
或其父类型的元素。? super T
表示比较器可以接受T
类型或者T
的任何父类型的对象作为输入参数。这个比较器用于确定流中元素的顺序,并找出最大值。
-
List<String> items = Arrays.asList("apple", "banana", "cherry");
Optional<String> maxLength = items.stream()
.collect(Collectors.maxBy(Comparator.comparingInt(String::length))); // 最大值
Optional<String> minLength = items.stream()
.collect(Collectors.minBy(Comparator.comparingInt(String::length))); // 最小值
maxLength.ifPresent(System.out::println); // "banana"
minLength.ifPresent(System.out::println); // "apple"
6. 统计以上所有:summarizingInt()、summarizingLong()、summarizingDouble()
-
统计以上所有:生成统计信息,包括总和、平均值、最小值、最大值。。
语法:
Collectors.summarizingInt(ToIntFunction<? super T> mapper)
Collectors.summarizingLong(ToIntFunction<? super T> mapper)
Collectors.summarizingDouble(ToIntFunction<? super T> mapper)
ToIntFunction<? super T> mapper
:用于从类型为 T
或其父类型的流元素中提取出一个 int
类型的数值。? super T
表示该函数可以接受 T
类型或者 T
的任何父类型的对象作为输入参数 。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
IntSummaryStatistics stats = numbers.stream().collect(Collectors.summarizingInt(e -> e));
System.out.println(stats);
System.out.println("Count: " + stats.getCount());
System.out.println("Sum: " + stats.getSum());
System.out.println("Average: " + stats.getAverage());
System.out.println("Min: " + stats.getMin());
System.out.println("Max: " + stats.getMax());
7. 连接:Joining()
将流中的元素连接成一个 String
,可以指定分隔符、前缀和后缀。
List<String> parts = Arrays.asList("Hello", "world", "!");
String result = parts.stream().collect(Collectors.joining(", ", "[", "]"));
System.out.println(result);
8. 分组:groupingBy(),partitioningBy() 分区
groupingBy()
- 功能:将流中的元素按照给定的分类函数(classifier function)进行分组。
- 返回类型:
Map<K, List<T>>
,其中K
是分类键,T
是流中元素的类型。结果是一个以分类键为键,以相同键的元素列表为值的Map
。 - 用法:适用于需要根据某种属性对流中的元素进行分组的场景。
partitioningBy()
- 功能:将流中的元素按照给定的谓词(predicate)进行二分分区,即根据谓词的结果(
true
或false
)来分组。 - 返回类型:
Map<Boolean, List<T>>
,其中Boolean
是谓词的结果,T
是流中元素的类型。结果是一个以true
和false
为键,以对应的元素列表为值的Map
。 - 用法:适用于需要将元素分为两个互补组的场景(例如,符合条件和不符合条件)。
// groupingBy()
@Data
@AllArgsConstructor
@NoArgsConstructor
class Person {
private String name;
private int age;
// 构造函数、getter和setter方法省略
}
@Test
void list() {
List<Person> people = Arrays.asList(
new Person("Alice", 17),
new Person("Bob", 17),
new Person("Charlie", 20),
new Person("David", 25)
);
Map<Integer, List<Person>> byAge = people.stream()
.collect(Collectors.groupingBy(Person::getAge)); // 按照年龄进行分组
System.out.println(byAge);
Map<Boolean, List<Person>> adultsAndMinors = items.stream()
.collect(Collectors.partitioningBy(p -> p.getAge() >= 18)); // 按照成年人未成年人进行分区
System.out.println(adultsAndMinors);
}
3.5 min(),max()
min
:返回流中按照给定比较器排序的最小元素。
语法:min(Comparator<? super T> comparator)
max
:返回流中按照给定比较器排序的最大元素。
语法:max(Comparator<? super T> comparator)
List<Integer> items = Arrays.asList(1, 2, 3, 4);
Optional<Integer> min = items.stream().min(Comparator.naturalOrder()); // 找到最小元素
min.ifPresent(System.out::println);
Optional<Integer> max = items.stream().max(Comparator.naturalOrder()); // 找到最大元素
max.ifPresent(System.out::println);
3.6 anyMatch(), allMatch(), noneMatch()
anyMatch
:如果流中的任何元素满足给定的条件,则返回true
。
语法:anyMatch(Predicate<? super T> predicate)
allMatch
:如果流中的所有元素都满足给定的条件,则返回true
。
语法:allMatch(Predicate<? super T> predicate)
noneMatch
:如果流中的没有任何元素满足给定的条件,则返回true
。
语法:noneMatch(Predicate<? super T> predicate)
List<String> items = Arrays.asList("A", "B", "C");
boolean anyStartsWithA = items.stream().anyMatch(item -> item.startsWith("A")); // 检查是否有以"A"开头的元素
System.out.println(anyStartsWithA);
boolean allStartsWithA = items.stream().allMatch(item -> item.startsWith("A")); // 检查是否所有元素以"A"开头
System.out.println(allStartsWithA);
boolean noneStartsWithZ = items.stream().noneMatch(item -> item.startsWith("Z")); // 检查是否没有以"Z"开头的元素
System.out.println(noneStartsWithZ);
3.7 findFirst(), findAny()
-
findFirst
:返回流中的第一个元素(按顺序)。 -
findAny
:返回流中的任意元素(可以是并行流中的任意元素)。
List<String> items = Arrays.asList("A", "B", "C");
Optional<String> first = items.stream().findFirst(); // 获取第一个元素
first.ifPresent(System.out::println);
Optional<String> any = items.parallelStream().findAny(); // 获取任意元素
any.ifPresent(System.out::println);
3.8 toArray()
-
将流中的元素收集到数组中。
语法:
toArray(IntFunction<T[]> generator
List<String> items = Arrays.asList("A", "B", "C");
String[] array = items.stream().toArray(String[]::new);
System.out.println("Array: " + Arrays.toString(array));
3.9 iterator()
- 返回流的迭代器,允许逐个访问流中的元素。
List<String> items = Arrays.asList("A", "B", "C");
Iterator<String> iterator = items.stream().iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
4、并行流
- 说明:
并行流是 Java 8 引入的 Stream API
的一个特性,它允许你利用多核处理器的计算能力,通过并行执行来加速对集合数据的操作。使用并行流可以提高性能,用于对集合进行处理。串行流是按顺序处理集合元素,而并行流则会将集合元素分成多个部分,并在多个线程上同时处理这些部分。 在某些情况下,并行流可能比串行流更高效,特别是在处理大量数据时。但是,并行流的效率也受到多种因素的影响,包括数据量大小、处理逻辑的复杂度、硬件资源等等。 通常情况下,并行流可以在多核处理器上更好地利用多线程处理数据,从而提高处理速度。但是,由于线程间的调度和同步开销,有时候并行流并不一定比串行流更快。
-
注意事项
-
线程安全:确保在并行流中使用的函数(如
map
、filter
中的 lambda 表达式)是线程安全的。 -
状态维护:并行流中避免使用非线程安全的状态,如在 lambda 表达式中使用外部的非 final 变量。
-
性能考量:并行流可能会引入线程管理的开销,对于小数据集,使用并行流可能反而比顺序流慢。
-
顺序依赖:并行流不保证元素的处理顺序,如果顺序很重要,应该使用顺序流。
-
@Test
void list() {
List<String> items = Arrays.asList("A", "B", "C");
// 顺序流/串行流
Stream<String> stream = items.stream();
stream.forEach(System.out::print);
System.out.println();
// 方式一: 直接获取并行的Stream流
Stream<String> stream1 = items.parallelStream();
stream1.forEach(System.out::print);
System.out.println();
//方式二: 通过Stream的静态方法parallel()获取并行的Stream流
Stream<String> stream3 = items.stream().parallel();
stream3.forEach(System.out::print);
// 顺序流计算
long start = System.currentTimeMillis();
int sum = IntStream.rangeClosed(1, 1000000000).sum();
long end = System.currentTimeMillis();
System.out.println("顺序流计算结果:" + sum + ",耗时:" + (end - start) + " 毫秒");
// 并行流计算
start = System.currentTimeMillis();
sum = IntStream.rangeClosed(1, 1000000000).parallel().sum();
end = System.currentTimeMillis();
System.out.println("并行流计算结果:" + sum + ",耗时:" + (end - start) + " 毫秒");
}
二、实际使用
@Test
void stream() {
List<String> items = Arrays.asList("A1", "B2", "C1", "C2");
String string = items.stream().filter(name ->
name.startsWith("C")
).collect(Collectors.joining(", ", "[", "]"));
System.out.println(string);
}
感谢博主:https://blog.csdn.net/listeningsea/article/details/123141350