引言
随着Java 8的发布,Stream API成为了Java开发人员手中的强大工具,它极大地简化了集合数据的操作,使得代码更简洁、更易于理解。本文将深入探讨Stream API的基本概念、优势以及如何使用它来处理数据。
Stream API简介
Stream API是Java 8引入的一项重要功能,它提供了一种新的处理数据的方式——流式处理(declarative programming)。与传统的循环迭代不同,Stream API允许开发者以声明式的方式来处理数据,这意味着开发者只需要告诉程序“要做什么”,而不是“怎么做”。
Stream API的关键特性
- 懒惰求值:Stream操作分为中间操作和终端操作,中间操作不会立即执行,只有当终端操作被触发时才会执行。
- 管道式编程:多个Stream操作可以串联起来形成流水线,这样可以减少代码量并提高可读性。
- 并行处理:Stream API天然支持并行处理,可以通过简单的设置实现对数据的并行处理,提高处理效率。
Stream API基本概念
数据源
- 集合框架 (
Collection
,Set
,List
) - 数组
- I/O通道
Stream
- 数据载体:Stream并不存储数据,而是承载数据。
- 操作数据:通过一系列方法调用来操作数据。
操作类型
- 中间操作:返回一个新的Stream,例如
filter()
,map()
,sorted()
等。 - 终端操作:执行计算并返回结果,例如
forEach()
,collect()
,reduce()
,count()
等。
使用示例
创建Stream
import java.util.Arrays;
import java.util.List;
public class StreamExample {
public static void main(String[] args) {
// 从集合创建Stream
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream = numbers.stream();
// 从数组创建Stream
Integer[] array = {1, 2, 3, 4, 5};
Stream<Integer> arrayStream = Arrays.stream(array);
// 从值创建Stream
Stream<Integer> valueStream = Stream.of(1, 2, 3, 4, 5);
}
}
中间操作
public class StreamIntermediateOperations {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 过滤偶数
Stream<Integer> evenNumbers = numbers.stream().filter(n -> n % 2 == 0);
// 映射转换
Stream<String> stringStream = numbers.stream().map(n -> n + " is a number");
// 排序
Stream<Integer> sortedStream = numbers.stream().sorted();
// 限制数量
Stream<Integer> limitedStream = numbers.stream().limit(3);
// 跳过前几个元素
Stream<Integer> skipStream = numbers.stream().skip(2);
}
}
终端操作
public class StreamTerminalOperations {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 遍历输出
numbers.stream().forEach(System.out::println);
// 收集到列表
List<Integer> squaredList = numbers.stream()
.map(n -> n * n)
.collect(Collectors.toList());
// 计算总和
int sum = numbers.stream().reduce(0, Integer::sum);
// 计算元素个数
long count = numbers.stream().count();
// 找到最大值
Optional<Integer> max = numbers.stream().max(Integer::compare);
// 找到最小值
Optional<Integer> min = numbers.stream().min(Integer::compare);
// 分组
Map<Boolean, List<Integer>> evenOddMap = numbers.stream()
.collect(Collectors.groupingBy(n -> n % 2 == 0));
// 归约
Optional<Integer> product = numbers.stream().reduce((a, b) -> a * b);
}
}
并行流
public class ParallelStreamExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 使用并行流
long parallelCount = numbers.parallelStream().count();
System.out.println("Parallel count: " + parallelCount);
}
}
性能考虑
虽然Stream API提供了很多便利,但在某些情况下,传统的循环可能更为高效。例如,在处理大数据集时,应当考虑是否需要并行流;对于小数据集,直接使用循环可能会更快。
用对象演示Stream流操作
前面的例子中我们都是使用Integer整数演示的,但是实际项目中我们更多的是使用对象去操作,下面我们通过对象演示一下。
首先,我们定义一个Person
类:
public class Person {
private String name;
private int age;
private boolean isStudent;
public Person(String name, int age, boolean isStudent) {
this.name = name;
this.age = age;
this.isStudent = isStudent;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public boolean isStudent() {
return isStudent;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", isStudent=" + isStudent +
'}';
}
}
接下来,我们将创建一个包含Person
对象的列表,并使用Stream API来处理这些对象。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class PersonStreamExample {
public static void main(String[] args) {
List<Person> people = new ArrayList<>(Arrays.asList(
new Person("Alice", 30, false),
new Person("Bob", 25, true),
new Person("Charlie", 22, true),
new Person("David", 27, false),
new Person("Eve", 20, true)
));
// 1. 过滤学生
List<Person> students = filterStudents(people);
printList(students);
// 2. 映射姓名
List<String> names = mapNames(people);
printList(names);
// 3. 排序按年龄
List<Person> sortedByAge = sortByAge(people);
printList(sortedByAge);
// 4. 限制前两个
List<Person> topTwo = limitTopTwo(people);
printList(topTwo);
// 5. 分组学生和非学生
Map<Boolean, List<Person>> groupedByStudentStatus = groupByStudentStatus(people);
printGroup(groupedByStudentStatus);
}
private static List<Person> filterStudents(List<Person> people) {
return people.stream()
.filter(person -> person.isStudent())
.collect(Collectors.toList());
}
private static List<String> mapNames(List<Person> people) {
return people.stream()
.map(Person::getName)
.collect(Collectors.toList());
}
private static List<Person> sortByAge(List<Person> people) {
return people.stream()
.sorted((p1, p2) -> Integer.compare(p1.getAge(), p2.getAge()))
.collect(Collectors.toList());
}
private static List<Person> limitTopTwo(List<Person> people) {
return people.stream()
.limit(2)
.collect(Collectors.toList());
}
private static Map<Boolean, List<Person>> groupByStudentStatus(List<Person> people) {
return people.stream()
.collect(Collectors.groupingBy(Person::isStudent));
}
private static void printList(List<?> list) {
list.forEach(System.out::println);
System.out.println();
}
private static void printGroup(Map<Boolean, List<Person>> group) {
group.forEach((key, value) -> {
System.out.println(key + ":");
value.forEach(System.out::println);
System.out.println();
});
}
}
这段代码演示了以下操作:
- 过滤学生:使用
filter()
方法找到所有学生。 - 映射姓名:使用
map()
方法提取每个人的姓名。 - 排序按年龄:使用
sorted()
方法按照年龄对人员进行排序。 - 限制前两个:使用
limit()
方法只保留前两个元素。 - 分组学生和非学生:使用
groupingBy()
方法根据是否是学生的状态对学生进行分组。
结论
Stream API是Java 8带来的重大革新之一,它不仅让代码更简洁、更易于维护,而且提高了代码的可读性和扩展性。掌握Stream API对于现代Java开发人员来说至关重要。