java8 stream的用法
Java 8 中的 Stream
API 引入了一种处理数据集合的强大且灵活的新方式。它允许你以声明式编程风格执行高效、可并行的操作,简化了集合操作和函数式编程。以下是一些基本的 Stream 使用方法:
-
创建流:
- 空流:
Stream.empty()
- 集合转换为流:调用
Collection
类型对象的stream()
方法,如List<String> list = ...; Stream<String> stream = list.stream();
- 数组转换为流:使用
Arrays.stream()
方法,例如int[] array = ...; IntStream stream = Arrays.stream(array);
- 生成流:通过
Stream.builder()
创建并添加元素,最后调用build()
得到流。
- 空流:
-
中间操作(Intermediate Operations):
- 过滤:
filter(Predicate predicate)
,根据给定的条件过滤流中的元素。stream.filter(s -> s.length() > 5);
- 映射:
map(Function mapper)
,将流中的每个元素应用一个函数,产生新的流。stream.map(String::toUpperCase);
- 排序:
sorted(Comparator comparator)
,对流中的元素进行排序。stream.sorted((s1, s2) -> s1.compareTo(s2));
- 截取或限制流长度:
limit(long maxSize)
,保留不超过指定数量的元素。stream.limit(10);
- 跳过元素:
skip(long n)
,丢弃前n个元素。stream.skip(5);
- 过滤:
-
终端操作(Terminal Operations):
- 遍历:
forEach(Consumer action)
,消费流中的每一个元素。stream.forEach(System.out::println);
- 收集:
collect(Collector collector)
,将流转换为另一种形式的结果,比如转换回集合、构建字符串等。List<String> collected = stream.collect(Collectors.toList());
- 查找与匹配:
findFirst()
,findAny()
,allMatch()
,anyMatch()
,noneMatch()
,用于查找满足条件的第一个/任意元素,或者判断所有/任意/无元素是否满足条件。 - 归约:
reduce(BinaryOperator accumulator)
或其他重载版本,将流中的元素聚合为单个结果。Optional<Integer> sum = stream.reduce((a, b) -> a + b);
- 遍历:
-
并行流: 可以通过调用
parallelStream()
方法来创建并行流,从而利用多核处理器并发处理流中的数据,提高性能。
在实际使用中,通常会结合这些操作符实现复杂的链式操作,比如筛选、转换、统计、分组等。流的设计使得它可以被惰性计算,只有在终端操作时才会真正执行所有的中间操作步骤。
案例
案例1:过滤并打印价格超过特定值的商品
import java.util.Arrays;
import java.util.List;
class Product {
String name;
double price;
// 构造函数、getters和setters省略...
}
public class StreamExample {
public static void main(String[] args) {
List<Product> productList = Arrays.asList(
new Product("Item1", 20.5),
new Product("Item2", 35.99),
new Product("Item3", 15.75),
new Product("Item4", 50.00)
);
double minPrice = 30.0;
productList.stream()
.filter(product -> product.getPrice() > minPrice)
.sorted(Comparator.comparing(Product::getPrice))
.map(Product::getName)
.forEach(System.out::println);
}
}
在这个例子中:
stream()
方法创建了一个商品对象流。filter()
方法用于过滤出价格大于给定阈值(minPrice
)的所有商品。sorted()
方法对结果进行排序,这里按商品价格升序排列。map()
方法将每个商品对象映射为其名称属性。forEach()
方法遍历并打印所有满足条件的商品名称。
案例2:计算集合元素数量
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
long count = names.stream().count();
System.out.println("Total number of names: " + count);
count()
方法用于统计流中的元素个数。
案例3:转换并求和
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
.mapToInt(Integer::intValue) // 或者直接使用 map(i -> i) 因为 Integer 已经是可变换成 int 的类型
.sum();
System.out.println("Sum of numbers: " + sum);
mapToInt()
将流中的 Integer 对象转换成 IntStream。sum()
方法计算流中所有数字的总和。
案例4:集合扁平化
假设我们有一个嵌套列表,需要将其扁平化为一个单一的列表:
List<List<String>> nestedLists = Arrays.asList(
Arrays.asList("A", "B"),
Arrays.asList("C", "D", "E"),
Arrays.asList("F")
);
List<String> flattenedList = nestedLists.stream()
.flatMap(Collection::stream)
.collect(Collectors.toList());
System.out.println("Flattened list: " + flattenedList);
flatMap()
方法用于将每个子列表转换为其流,并将所有这些流合并成一个单一的流。
案例5:分组和统计
考虑一个学生类及其成绩数据,按学科分组并计算各组的平均成绩:
class Student {
String name;
String subject;
int score;
// 构造函数、getters和setters省略...
}
List<Student> students = Arrays.asList(
new Student("Alice", "Math", 90),
new Student("Bob", "Math", 85),
new Student("Charlie", "English", 95),
new Student("David", "Math", 78),
new Student("Alice", "English", 92)
);
Map<String, Double> averageScoresBySubject = students.stream()
.collect(Collectors.groupingBy(Student::getSubject,
Collectors.averagingInt(Student::getScore)));
averageScoresBySubject.forEach((subject, avgScore) -> System.out.println(subject + ": " + avgScore));
groupingBy()
方法用于根据提供的函数(这里是获取学生的学科)对流中的元素进行分组。- 第二个参数是收集器,它定义了如何处理每个组内的元素。在这里,我们使用
averagingInt()
来计算每组内的平均分数。
案例6:查找最大值或最小值
List<Integer> numbers = Arrays.asList(10, 20, 30, 40, 50, -10);
OptionalInt maxNumber = numbers.stream().mapToInt(Integer::intValue).max();
maxNumber.ifPresent(System.out::println); // 输出最大的整数
OptionalInt minNumber = numbers.stream().mapToInt(Integer::intValue).min();
minNumber.ifPresent(System.out::println); // 输出最小的整数
max()
和min()
方法用于从 IntStream 中找到最大和最小值,并返回 OptionalInt 对象以处理可能为空的情况。
案例7:过滤并映射到新的对象集合
假设我们有一个 Person
类,并且想根据年龄筛选出大于18岁的人员列表,并将他们转换为只包含姓名的字符串列表:
class Person {
String name;
int age;
// 构造函数、getters和setters省略...
}
List<Person> people = Arrays.asList(
new Person("Alice", 25),
new Person("Bob", 16),
new Person("Charlie", 30),
new Person("David", 19)
);
List<String> namesOfAdults = people.stream()
.filter(person -> person.getAge() > 18)
.map(Person::getName)
.collect(Collectors.toList());
System.out.println("Names of adults: " + namesOfAdults);
filter()
方法用于基于给定条件(这里是年龄大于18岁)过滤出人员。map()
方法用于将每个满足条件的Person
对象映射成其名字,即转换为String
类型。
案例8:连接字符串
如果我们有一个字符串列表,并希望用特定字符连接它们成为一个单个字符串:
List<String> words = Arrays.asList("Java", "Stream", "API", "Examples");
String concatenatedWords = words.stream()
.collect(Collectors.joining(", "));
System.out.println("Concatenated words: " + concatenatedWords);
Collectors.joining()
是一个收集器,它接收一个分隔符参数,并将流中的所有字符串连接起来。在这个例子中,我们使用 ", " 作为分隔符来创建一个逗号分隔的单词列表。