一、Stream流的特点和使用过程
Stream API允许开发者以声明性方式处理数据集合。可以简化复杂的数据操作,并且支持并行处理以提高性能。
1.1 特点
- 声明性: Stream API允许你描述你想要做什么,而不是详细说明怎么做。
- 链式操作: 可将多个操作链接在一起,形成一个流水线,每个操作都会生成一个新的流供下一个操作使用。
- 函数式编程: Stream API鼓励使用无副作用的函数和 lambda 表达式。
- 并行处理: Stream API支持并行流,可以充分利用多核处理器。
- 延迟执行: Stream 操作是惰性的,只有在终端操作(如 collect、forEach)被调用时,整个流水线才会执行。
- 短路操作: 某些终端操作(如 anyMatch、allMatch、noneMatch、findFirst)在找到结果后会立即停止处理。
1.2 使用过程
我们先来了解使用Stream流的四个过程,然后再逐渐深入了解每个过程分别都有哪些骚操作。
1.2.1 创建流
从数据源(如集合、数组、文件等)创建一个流。
java
// 创建Stream
ArrayList<Integer> list = Lists.newArrayList(1, 2, 3, 4, 5, 6);
Stream<Integer> stream = list.stream();
1.2.2 中间操作
对流进行一系列转换操作,如 filter(过滤)、map(映射)、sorted(排序)等。这些操作会返回一个新的流,不会立即执行。
java
// 中间操作,比如映射、过滤、排序等,这里我们以映射为例,将每个元素+1
Stream<Integer> integerStream = stream.map(i -> i + 1);
1.2.3 终端操作
执行一个终端操作来结束流的处理并产生结果。终端操作会触发整个流水线的执行,并且不会返回一个新的流。
java
// 终端操作,比如收集、聚合、forEach等,这里我们以收集为例
List<Integer> collect = integerStream.collect(Collectors.toList());
1.2.4 处理结果
使用终端操作返回的结果进行后续处理。
java
// 处理结果
collect.forEach(System.out::println);
流只能被使用一次。一旦终端操作被触发,流就会被关闭,无法再次使用。
二、Stream流的魅力
2.1 需求背景
假如现在有一群整型数据,需求是:
- 提取出奇数和偶数
- 找到奇数和偶数各自的最大值和最小值
2.1 没有使用Stream前
java
public class Main {
public static void main(String[] args) {
// 原始数据
List<Integer> list =Arrays.asList(1, 2, 3, 4, 5, 6);
Map<Boolean, List<Integer>> map = new HashMap<>(2);
// 创建数数集合和偶数集合
List<Integer> ji = new ArrayList<>();
List<Integer> ou = new ArrayList<>();
// 遍历集合,将偶数放入偶数集合,将奇数放入奇数集合
for (Integer i : list) {
if (i % 2 == 0) {
ou.add(i);
} else {
ji.add(i);
}
}
// 各自排序
ji.sort(Integer::compareTo);
ou.sort(Integer::compareTo);
// 奇数数组找到最大值和最小值
if(ji.size() > 1) {
map.put(Boolean.FALSE, Arrays.asList(ji.get(0), ji.get(ji.size() - 1)));
} else if (ji.size() == 1) {
map.put(Boolean.FALSE, Arrays.asList(ji.get(0), ji.get(0)));
} else {
map.put(Boolean.FALSE, Collections.emptyList());
}
// 偶数数组找到最大值和最小值
if(ou.size() > 1) {
map.put(Boolean.TRUE, Arrays.asList(ou.get(0), ou.get(ou.size() - 1)));
} else if (ou.size() == 1) {
map.put(Boolean.TRUE, Arrays.asList(ou.get(0), ou.get(0)));
} else {
map.put(Boolean.TRUE, Collections.emptyList());
}
// 奇数数组,偶数数组,各自的最大值和最小值
// 输出结果:奇数数组和偶数数组的最小值和最大值
map.forEach((k, v) -> System.out.println((k ? "偶数" : "奇数") + "数组: 最小值 = " + v.get(0) + ", 最大值 = " + v.get(1)));
}
}
输出结果:
java
奇数数组: 最小值 = 1, 最大值 = 5
偶数数组: 最小值 = 2, 最大值 = 6
2.2 有使用Stream流后
我们可以发现当我们有使用Stream流后代码的简洁的会提高不少。
java
public class Main {
public static void main(String[] args) {
Map<Boolean, List<Integer>> map = Stream.of(1, 2, 3, 4, 5, 6)
.collect(Collectors.groupingBy(
x -> x % 2 == 0,
Collectors.collectingAndThen(
Collectors.toList(),
list -> {
Collections.sort(list);
return Arrays.asList(list.get(0), list.get(list.size() - 1));
}
)
));
// 输出结果:奇数数组和偶数数组的最小值和最大值
map.forEach((k, v) -> System.out.println((k ? "偶数" : "奇数") + "数组: 最小值 = " + v.get(0) + ", 最大值 = " + v.get(1)));
}
}
输出结果:
java
奇数数组: 最小值 = 1, 最大值 = 5
偶数数组: 最小值 = 2, 最大值 = 6
三、Steam流创建
3.1 通过集合创建
调用集合对象的stream()方法来获取一个流
java
public class Main {
public static void main(String[] args) {
// 调用集合对象创建的stream获取一个流
Stream<String> stream = Arrays.asList("a", "b", "c").stream();
stream.forEach(System.out::println);
}
}
输出结果:
java
a
b
c
3.2 通过数组创建
使用Arrays.stream()方法从数组创建流,这适用于任何类型的数组。基本类型的数组,Arrays.stream()会返回特定类型的流,如IntStream、LongStream或DoubleStream。如果需要将这些流转换为通用Stream,你可以使用boxed()方法。
java
public class Main {
public static void main(String[] args) {
int[] array = {1, 2, 3, 4, 5};
IntStream intStream = Arrays.stream(array);
intStream.min().ifPresent(System.out::println);
String[] strArray = {"a", "b", "c"};
Stream<String> stringStream = Arrays.stream(strArray);
System.out.println(stringStream.collect(Collectors.toList()));
}
}
输出结果:
java
1
[a, b, c]
3.3 通过 Stream 静态方法创建
Stream类提供了几个静态方法来创建流。
java
public class Main {
public static void main(String[] args) {
Stream<String> strStream = Stream.of("a", "b", "c");
strStream.forEach(System.out::println);
}
}
输出结果:
java
a
b
c
3.4 通过随机数生成
Random类可以用于生成随机数,并且你可以使用ints()、longs()或doubles()方法创建一个流。
java
public class Main {
public static void main(String[] args) {
// 生成10个0到100之间的随机数
IntStream ints = new Random().ints(10, 0, 100);
ints.forEach(System.out::println);
}
}
输出结果:
java
56
20
47
19
83
0
28
92
95
3
3.5 通过文件I/O
在处理文件时,可以使用Files类中的方法,如lines(),从文件中读取行并创建一个流。
java
public class Main {
public static void main(String[] args) {
try {
Path path = Paths.get("path/to/file.txt");
Stream<String> linesStream = Files.lines(path);
} catch (IOException e) {
e.printStackTrace();
}
}
}
输出结果:
111
222
333
3.6 无限流创建
Stream API 提供了能够生成无限序列的流,但通常会结合limit()方法来限制流的长度。
- Stream.iterate(T seed, UnaryOperatorf): 创建一个无限顺序的流,通过反复应用函数f来生成元素。
- Stream.generate(Suppliers): 创建一个无限无序的流,其中每个元素由提供的Supplier生成。
java
public class Main {
public static void main(String[] args) {
// 使用iterate生成一个无限递增的整数流
Stream<Integer> intStream = Stream.iterate(0, i -> i + 1).limit(10);
intStream.forEach(System.out::println);
}
}
输出结果:
java
0
1
2
3
4
5
6
7
8
9
3.7 通过范围创建
创建一个包含从startInclusive(包含)到endExclusive(不包含)之间的整数的流。
java
public class Main {
public static void main(String[] args) {
// 使用generate生成一个无限的随机数流
IntStream intStream = Stream.generate(Math::random).limit(10).mapToInt(x -> (int) (x * 100));
intStream.forEach(System.out::println);
}
}
输出结果:
java
54
46
57
43
20
98
46
20
9
68
四、中间操作
4.1 filter(过滤)
filter方法用于过滤流中的元素。
java
@Test
void processStream() {
// 使用filter过滤出非空元素
List<String> collect = Arrays.asList("a", "b", "c", null, "d", null)
.stream()
.filter(Objects::nonNull)
.collect(Collectors.toList());
collect.forEach(System.out::println);
}
输出结果:
java
abcd
4.2 map(转换)
map方法用于对流中的每个元素执行某种操作,并返回一个新的流。
java
public class Main {
public static void main(String[] args) {
// 使用filter过滤出非空元素,并且对每个字符加上"-"
List<String> collect = Arrays.asList("a", "b", "c", null, "d", null)
.stream()
.filter(Objects::nonNull)
.map(x -> "-" + x + "-")
.collect(Collectors.toList());
collect.forEach(System.out::println);
}
}
输出结果:
java
-a-
-b-
-c-
-d-
此外streamAPI中还有指定类型的map方法:
- mapToInt(ToIntFunction<? super T> mapper): 将流中的元素转换成int类型。
- mapToLong(ToLongFunction<? super T> mapper): 将流中的元素转换成long类型。
- mapToDouble(ToDoubleFunction<? super T> mapper): 将流中的元素转换成double类型。
4.3 flatMap(转换)
flatMap方法在Java Stream API中用于将流中的每个元素转换成一个新的流,然后将这些新生成的流合并成一个单一的流。用于处理流中的集合或数组元素,以将它们"展平"成一个单一的元素流。
一个包含字符串列表的列表使用flatMap将其转换成一个包含所有字符串的单一流:
java
public class Main {
public static void main(String[] args) {
List<List<String>> list = Arrays.asList(Arrays.asList("a", "b"), Arrays.asList("b", "c"), Arrays.asList("c", "d"));
List<String> collect = list.stream()
.flatMap(x -> x.stream())
.collect(Collectors.toList());
collect.forEach(System.out::print);
}
}
输出结果:
java
abbccd
4.4 distince(去重)
distinct方法用于去除流中的重复元素。基于元素的 equals 和 hashCode 方法来确定哪些元素是重。
java
public class Main {
public static void main(String[] args) {
List<List<String>> list = Arrays.asList(Arrays.asList("a", "b"), Arrays.asList("b", "c"), Arrays.asList("c", "d"));
List<String> collect = list.stream()
.flatMap(x -> x.stream())
.distinct()
.collect(Collectors.toList());
collect.forEach(System.out::print);
}
}
输出结果:
java
abcd
4.5 Limit(限制)/Skip(跳过)/Peek(展示)
limit用于限制流中的元素数量,skip用于跳过流中的前N个元素,而peek则允许对流中的每个元素执行某种操作(如打印、修改等)而不改变流本身。peek通常用于调试或查看流中的元素。
java
public class Main {
public static void main(String[] args) {
List<List<String>> list = Arrays.asList(Arrays.asList("a", "b"), Arrays.asList("b", "c"), Arrays.asList("c", "d"));
List<String> collect = list.stream()
.flatMap(x -> x.stream())
.distinct()
.skip(2)
.limit(2)
.peek(System.out::print)
.collect(Collectors.toList());
System.out.println();
System.out.println(collect);
}
}
输出结果:
java
cd
[c, d]
4.6 Sorted(排序)
排序可以通过sorted()方法实现,该方法有两种形式:
- 无参的sorted(),它使用元素的自然顺序进行排序(要求元素实现Comparable接口);
- 以及接受Comparator参数的sorted(Comparator<? super T> comparator),它允许自定义排序规则。
java
public class Main {
public static void main(String[] args) {
// 1.使用无限流生成10个1 ~ 100 范围内的随机数 升序打印
IntStream sorted1 = new Random().ints(10, 1, 100).sorted();
sorted1.forEach(x -> System.out.print(x + "-"));
System.out.println();
// 2.自定义排序规则,降序打印
List<Integer> numbers = Arrays.asList(5, 2, 9, 1, 7);
List<Integer> collect = numbers.stream().sorted((x, y) -> y - x).collect(Collectors.toList());
collect.forEach(x -> System.out.print(x + "-"));
}
}
输出结果:
java
24-32-32-33-41-49-62-63-68-88-
9-7-5-2-1-
4.7 concat(两个流连接成一个流)
concat(Stream<? extends T> a, Stream<? extends T> b): 静态方法,用于将两个流连接成一个流。
java
public class Main {
public static void main(String[] args) {
// 创建两个整数列表
List<Integer> list1 = Arrays.asList(1, 2, 3);
List<Integer> list2 = Arrays.asList(4, 5, 6);
// 将两个列表转换为流,并使用 concat 方法连接它们
Stream<Integer> concatenatedStream = Stream.concat(list1.stream(), list2.stream());
// 使用连接后的流进行一些操作,比如打印所有元素
concatenatedStream.forEach(System.out::println);
}
}
输出结果:
java
123456
五、终端操作
5.1 forEach/findFirst/findAny(查询)
Stream API中,forEach、findFirst和findAny都是终端操作。forEach用于迭代流中的每个元素并执行一个操作,findFirst用于获取流中的第一个元素,而findAny则用于获取流中的任意元素(并行流特别有用,因为它可能更快)。
java
public class Main {
public static void main(String[] args) {
List<String> list = Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h");
// 使用forEach循环
list.stream().forEach(System.out::print);
System.out.println();
// 使用findFirst找到集合中第一个元素
String orElse = list.stream().findFirst().orElse(null);
System.out.print(orElse);
System.out.println();
// 使用findAny找到任意一个元素
String anElse = list.stream().findAny().orElse(null);
System.out.print(anElse);
System.out.println();
}
}
输出结果如下:
java
abcdefgh
a
a
5.2 count/sum/max/min(汇总)
count、sum、max和min都是终端操作,用于对流中的元素进行计数、求和、找最大值和最小值。
java
public class Main {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
// 使用count统计集合元素个数
long count = list.stream().count();
System.out.println("集合元素个数为:" + count);
// 使用sum统计集合元素总和
int sum = list.stream().mapToInt(Integer::intValue).sum();
System.out.println("集合元素总和:" + sum);
// 使用max找到集合元素最大值
int max = list.stream().mapToInt(Integer::intValue).max().orElse(0);
System.out.println("集合元素最大值:" + max);
// 使用min找到集合元素最小值
int min = list.stream().mapToInt(Integer::intValue).min().orElse(0);
System.out.println("集合元素最小值:" + min);
}
}
输出结果如下:
java
集合元素个数为:16
集合元素总和:136
集合元素最大值:16
集合元素最小值:1
5.3 anyMatch/allMatch/noneMatch(匹配)
anyMatch、allMatch和noneMatch是终端操作,用于检查流中的元素是否满足给定的谓词(条件)。anyMatch检查是否有任何元素满足条件,allMatch检查是否所有元素都满足条件,而noneMatch检查是否没有任何元素满足条件。
java
public class Main {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
// 使用 anyMatch 判断 大于5的元素 是否存在
boolean anyMatch = list.stream().mapToInt(Integer::valueOf).anyMatch(x -> x > 5);
System.out.println("是否存在大于5的元素:" + anyMatch);
// 使用 allMatch 判断 集合中的元素是否全部大于 5
boolean allMatch = list.stream().mapToInt(Integer::valueOf).allMatch(x -> x > 5);
System.out.println("是否全部元素大于5:" + allMatch);
// 使用 noneMatch 判断 集中中的元素是否都不满足 不为 null
List<Integer> list1 = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, null, 11, 12, 13, 14, 15, 16);
boolean noneMatch = list1.stream().noneMatch(x -> x != null);
System.out.println("集合中元素是否都不为null:" + noneMatch);
}
}
输出结果如下:
java
是否存在大于5的元素:true
是否全部元素大于5:false
集合中元素是否都不为null:false
5.4 reduce(归约)
reduce方法是一个终端操作,用于将流中的所有元素组合成一个单一的结果。它通常用于执行某种累积操作,比如计算元素的总和、乘积或连接字符串等。
reduce(T identity, BinaryOperatoraccumulator) 此方法接受一个初始值和一个累积函数,用于归约流中的元素。
reduce(BinaryOperatoraccumulator) 此方法不接受初始值,而是使用流中的第一个元素作为初始值,然后应用累积函数。
计算一个员工列表中所有员工的总薪水,同时找出薪水最高的员工。
java
public class Main {
public static void main(String[] args) {
// 创建一个员工列表
List<Employee> employees = Arrays.asList(
new Employee("Alice", 5000.0),
new Employee("Bob", 6000.0),
new Employee("Charlie", 5500.0),
new Employee("David", 6500.0)
);
// 计算总薪资
Double reduce = employees.stream().reduce(0.0, (acc, employee) -> acc + employee.getSalary(), Double::sum);
System.out.println("各个员工总薪资为:" + reduce);
// 找到薪资最高的员工
Employee employee = employees.stream().max(Comparator.comparingDouble(Employee::getSalary)).orElse(null);
System.out.println("薪资最高的员工薪资为:" + employee.getSalary());
}
}
@AllArgsConstructor
@Data
class Employee {
private String name;
private double salary;
}
输出结果如下:
java
各个员工总薪资为:23000.0
薪资最高的员工薪资为:6500.0
5.5 collect(收集)
collect方法在Java Stream API中通常用于收集流中的元素到某种集合或其他数据结构中。
collect(Suppliersupplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner):
重载版本的collect方法提供了更高的灵活性,允许你自定义收集过程。 这个collect方法接受三个参数:
Suppliersupplier:一个供应器,用于创建新的结果容器。
BiConsumer<R, ? super T> accumulator:一个累加器,用于将流中的元素添加到结果容器中。
BiConsumer<R, R> combiner:一个组合器,用于合并两个结果容器(通常用于并行流)。
自定义一个收集过程,将流中的字符串连接成一个单独的字符串:
java
public class Main {
public static void main(String[] args) {
// 创建一个员工列表
List<Employee> employees = Arrays.asList(
new Employee("Alice", 5000.0),
new Employee("Bob", 6000.0),
new Employee("Charlie", 5500.0),
new Employee("David", 6500.0)
);
String string = employees.stream()
.map(Employee::getName)
.collect(
StringBuilder::new,
(sb, name) -> {
if (!sb.isEmpty()) {
sb.append(" - ");
}
sb.append(name);
},
StringBuilder::append
).toString();
System.out.println(string);
}
}
@AllArgsConstructor
@Data
class Employee {
private String name;
private double salary;
}
输出结果如下:
java
Alice - Bob - Charlie - David
5.6 toList/toMap/toSet/toArray(转集合)
toList(), toMap(), 和 toSet() 是非常有用的终端操作,它们可以将流中的元素收集到相应的集合中
java
public class Main {
public static void main(String[] args) {
// 创建一个员工列表
List<Employee> employees = Arrays.asList(
new Employee("Alice", 5000.0),
new Employee("Bob", 6000.0),
new Employee("Charlie", 5500.0),
new Employee("David", 6500.0)
);
// toList
List<String> list = employees.stream().map(Employee::getName).collect(Collectors.toList());
System.out.println(list);
// toSet
Set<String> set = employees.stream().map(Employee::getName).collect(Collectors.toSet());
System.out.println(set);
// toMap
Map<String, Employee> collect = employees.stream().collect(Collectors.toMap(Employee::getName, Function.identity()));
System.out.println(collect);
}
}
@AllArgsConstructor
@Data
class Employee {
private String name;
private double salary;
}
输出结果如下:
java
[Alice, Bob, Charlie, David]
[Bob, Alice, Charlie, David]
{Bob=Employee(name=Bob, salary=6000.0), Alice=Employee(name=Alice, salary=5000.0), Charlie=Employee(name=Charlie, salary=5500.0), David=Employee(name=David, salary=6500.0)}
5.7 summing/averaging/summarizing(统计)
Collectors 类提供了几个用于数据统计的收集器,如 averagingDouble、summarizingDouble 和 summingDouble。这些收集器通常与流的 collect 方法一起使用,用于对数值流(如员工薪水)进行统计。 Collectors提供了一系列用于数据统计的静态方法: 计数:count 平均值:averagingInt、averagingLong、averagingDouble 最值:maxBy、minBy 求和:summingInt、summingLong、summingDouble 统计以上所有:summarizingInt、summarizingLong、summarizingDouble
java
public class Main {
public static void main(String[] args) {
// 创建一个员工列表
List<Employee> employees = Arrays.asList(
new Employee("Alice", 5000.0),
new Employee("Bob", 6000.0),
new Employee("Charlie", 5500.0),
new Employee("David", 6500.0)
);
// 统计平均薪水
Double averaging = employees.stream().collect(Collectors.averagingDouble(Employee::getSalary));
System.out.println(averaging);
// 统计薪资总和
Double sum = employees.stream().collect(Collectors.summingDouble(Employee::getSalary));
System.out.println(sum);
// 获取薪资的统计信息(包含:count, sum, min, average, max)
DoubleSummaryStatistics collect = employees.stream().collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(collect);
}
}
@AllArgsConstructor
@Data
class Employee {
private String name;
private double salary;
}
输出结果如下:
java
5750.0
23000.0
DoubleSummaryStatistics{count=4, sum=23000.000000, min=5000.000000, average=5750.000000, max=6500.000000}
5.8 summaryStatistics(统计)
对于数值流(如 IntStream, LongStream, DoubleStream),summaryStatistics返回描述该流统计信息的对象,如最小值、最大值、平均值等。
java
public class Main {
public static void main(String[] args) {
// 创建一个包含一些双精度浮点数的数组
double[] numbers = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0};
// 使用 DoubleStream 的 of 方法创建一个流,然后调用 summaryStatistics 方法
DoubleSummaryStatistics stats = Arrays.stream(numbers).summaryStatistics();
// 输出统计信息
System.out.println("最小值: " + stats.getMin());
System.out.println("最大值: " + stats.getMax());
System.out.println("平均值: " + stats.getAverage());
System.out.println("元素数量: " + stats.getCount());
System.out.println("元素总和: " + stats.getSum());
}
}
输出结果如下:
java
最小值: 1.0
最大值: 10.0
平均值: 5.5
元素数量: 10
元素总和: 55.0
5.9 joining(拼接)
Collectors.joining可以将流中的元素连接成一个字符串。这对将列表、集合或其他流数据结构转换为单个字符串表示形式特别有用。
java
public class Main {
public static void main(String[] args) {
// 创建一个员工列表
List<Employee> employees = Arrays.asList(
new Employee("Alice", 5000.0),
new Employee("Bob", 6000.0),
new Employee("Charlie", 5500.0),
new Employee("David", 6500.0)
);
// 将各个员工的名称用 - 符号连接起来
String collect = employees.stream().map(Employee::getName).collect(Collectors.joining("-"));
System.out.println(collect);
}
}
@AllArgsConstructor
@Data
class Employee {
private String name;
private double salary;
}
输出结果如下:
java
Alice-Bob-Charlie-David
5.10 分组(partitioningBy/groupingBy)
Collectors.partitioningBy 方法用于根据提供的谓词(Predicate)对流中的元素进行分区。
Collectors.groupingBy 方法用于根据提供的分类函数对流中的元素进行分组。在这个例子中,分类函数是 Employee::getDepartment,它根据员工的部门对员工进行分组。结果是一个映射,其中键是部门名称,值是对应部门的员工列表。
java
public class Main {
public static void main(String[] args) {
// 创建一个员工列表
List<Employee> employees = Arrays.asList(
new Employee("Alice", 5000.0, "软件按部"),
new Employee("Bob", 6000.0, "市场部"),
new Employee("Charlie", 5500.0, "后勤部"),
new Employee("David", 6500.0,"软件按部")
);
// 使用partitioning 自定义分区 这里我们以薪资大于5000为分区
Predicate<Employee> salaryAbove5000 = employee -> employee.getSalary() > 5000;
Map<Boolean, List<Employee>> collect = employees.stream().collect(Collectors.partitioningBy(salaryAbove5000));
System.out.println(collect);
// 按员工属性【部门】进行分区
Map<String, List<Employee>> collect1 = employees.stream().collect(Collectors.groupingBy(Employee::getDepartment));
System.out.println(collect1);
}
}
@AllArgsConstructor
@Data
class Employee {
private String name;
private double salary;
private String department;
}
输出结果如下:
java
{false=[Employee(name=Alice, salary=5000.0, department=软件按部)], true=[Employee(name=Bob, salary=6000.0, department=市场部), Employee(name=Charlie, salary=5500.0, department=后勤部), Employee(name=David, salary=6500.0, department=软件按部)]}
{后勤部=[Employee(name=Charlie, salary=5500.0, department=后勤部)], 市场部=[Employee(name=Bob, salary=6000.0, department=市场部)], 软件按部=[Employee(name=Alice, salary=5000.0, department=软件按部), Employee(name=David, salary=6500.0, department=软件按部)]}
六、顺序流和并行流
parallel和sequential是用来指定流的执行模式的方法。这两种模式决定了流中的元素是如何被处理的。
6.1 sequential(顺序流)
对一个流调用sequential()时,这个流以顺序方式执行操作。顺序流中的元素按照它们在数据源中出现的顺序逐个进行处理。顺序流是在单个线程中执行的,因此不存在线程安全问题。
java
public class StreamPerformanceComparison {
public static void main(String[] args) {
int n = 100; // 数据规模
// 测试顺序流
long sequentialStart = System.currentTimeMillis();
// 模拟耗时操作
IntStream.range(1, n + 1)
.sequential()
.forEach(StreamPerformanceComparison::process);
long sequentialEnd = System.currentTimeMillis();
System.out.println("Sequential time: " + (sequentialEnd - sequentialStart) + " ms");
}
// 模拟耗时操作
private static void process(int i) {
try {
Thread.sleep(10); // 模拟每个元素需要 10 毫秒处理时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
输出结果如下:
java
Sequential time: 1544 ms
5.2 parallel(并行流)
调用parallelStream()或者对一个已经存在的流调用parallel()时,这个流以并行方式执行操作。并行流会尝试利用多个线程来同时处理多个元素,以提高处理速度。并行流是基于Java的ForkJoinPool实现的,它是一个特殊的线程池,适合执行可以并行处理的任务。
java
public class StreamPerformanceComparison {
public static void main(String[] args) {
int n = 100; // 数据规模
// 测试并行流
long parallelStart = System.currentTimeMillis();
// 模拟耗时操作
IntStream.range(1, n + 1)
.parallel()
.forEach(StreamPerformanceComparison::process);
long parallelEnd = System.currentTimeMillis();
System.out.println("Parallel time: " + (parallelEnd - parallelStart) + " ms");
}
// 模拟耗时操作
private static void process(int i) {
try {
Thread.sleep(10); // 模拟每个元素需要 10 毫秒处理时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
输出结果如下:
java
Parallel time: 61 ms