Java Stream 多方法组合处理数据
2022-07-195.7k 阅读
Java Stream 概述
Java 8 引入了 Stream API,它为处理集合数据提供了一种更为高效和简洁的方式。Stream 不是数据结构,它并不存储数据,而是在数据源(如集合、数组等)上执行一系列操作。Stream 操作可以分为中间操作和终端操作,中间操作返回一个新的 Stream,允许链式调用多个中间操作;终端操作执行 Stream 操作链,并返回结果或副作用。
Stream 创建
- 从集合创建 集合接口都有 stream() 和 parallelStream() 方法。stream() 方法创建顺序流,parallelStream() 创建并行流。例如:
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class StreamCreationFromCollection {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
// 创建顺序流
Stream<Integer> sequentialStream = numbers.stream();
// 创建并行流
Stream<Integer> parallelStream = numbers.parallelStream();
}
}
- 从数组创建 可以使用 Arrays.stream() 方法从数组创建 Stream。
import java.util.Arrays;
import java.util.stream.Stream;
public class StreamCreationFromArray {
public static void main(String[] args) {
int[] intArray = {1, 2, 3};
Stream<int[]> streamOfArrays = Stream.of(intArray);
// 基本类型特化流
java.util.stream.IntStream intStream = Arrays.stream(intArray);
}
}
- 使用 Stream.of() 可以直接使用 Stream.of() 方法创建 Stream,传入可变参数。
import java.util.stream.Stream;
public class StreamCreationWithOf {
public static void main(String[] args) {
Stream<String> stream = Stream.of("a", "b", "c");
}
}
中间操作
- 过滤 (filter) filter 方法用于根据条件过滤 Stream 中的元素。它接受一个 Predicate 作为参数,该 Predicate 对每个元素进行判断,返回 true 的元素会被保留在新的 Stream 中。
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class StreamFilter {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
List<Integer> filteredNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println(filteredNumbers); // 输出: [2, 4]
}
}
- 映射 (map) map 方法将 Stream 中的每个元素按照给定的 Function 进行转换,生成一个新的 Stream。例如,将字符串转换为其长度。
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class StreamMap {
public static void main(String[] args) {
List<String> words = new ArrayList<>();
words.add("apple");
words.add("banana");
words.add("cherry");
List<Integer> wordLengths = words.stream()
.map(String::length)
.collect(Collectors.toList());
System.out.println(wordLengths); // 输出: [5, 6, 6]
}
}
- 扁平映射 (flatMap) flatMap 方法与 map 类似,但它用于处理嵌套结构。它将每个元素映射为一个 Stream,然后将这些 Stream 扁平化为一个单一的 Stream。例如,处理一个包含多个列表的列表。
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class StreamFlatMap {
public static void main(String[] args) {
List<List<Integer>> nestedLists = new ArrayList<>();
List<Integer> list1 = new ArrayList<>();
list1.add(1);
list1.add(2);
List<Integer> list2 = new ArrayList<>();
list2.add(3);
list2.add(4);
nestedLists.add(list1);
nestedLists.add(list2);
List<Integer> flatList = nestedLists.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
System.out.println(flatList); // 输出: [1, 2, 3, 4]
}
}
- 排序 (sorted) sorted 方法用于对 Stream 中的元素进行排序。可以使用自然排序(如果元素实现了 Comparable 接口),也可以传入一个 Comparator 进行自定义排序。
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
public class StreamSorted {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(3);
numbers.add(1);
numbers.add(2);
// 自然排序
List<Integer> sortedNumbers = numbers.stream()
.sorted()
.collect(Collectors.toList());
System.out.println(sortedNumbers); // 输出: [1, 2, 3]
// 自定义排序
List<Integer> reverseSortedNumbers = numbers.stream()
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
System.out.println(reverseSortedNumbers); // 输出: [3, 2, 1]
}
}
- 去重 (distinct) distinct 方法用于去除 Stream 中的重复元素。它通过对象的 equals 方法来判断元素是否重复。
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class StreamDistinct {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(2);
numbers.add(3);
List<Integer> distinctNumbers = numbers.stream()
.distinct()
.collect(Collectors.toList());
System.out.println(distinctNumbers); // 输出: [1, 2, 3]
}
}
- 限制 (limit) limit 方法用于截取 Stream 中的前 n 个元素,返回一个新的 Stream。
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class StreamLimit {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);
List<Integer> limitedNumbers = numbers.stream()
.limit(3)
.collect(Collectors.toList());
System.out.println(limitedNumbers); // 输出: [1, 2, 3]
}
}
- 跳过 (skip) skip 方法用于跳过 Stream 中的前 n 个元素,返回剩余元素组成的新 Stream。
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class StreamSkip {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);
List<Integer> skippedNumbers = numbers.stream()
.skip(2)
.collect(Collectors.toList());
System.out.println(skippedNumbers); // 输出: [3, 4, 5]
}
}
终端操作
- 收集 (collect) collect 方法用于将 Stream 中的元素收集到一个集合中,或者生成一个汇总结果。Collectors 类提供了许多静态方法来支持不同类型的收集操作。
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class StreamCollect {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
// 收集到 List
List<Integer> collectedList = numbers.stream()
.collect(Collectors.toList());
// 收集到 Set
Set<Integer> collectedSet = numbers.stream()
.collect(Collectors.toSet());
// 按奇偶性分组
Map<Boolean, List<Integer>> groupedByParity = numbers.stream()
.collect(Collectors.groupingBy(n -> n % 2 == 0));
}
}
- 归约 (reduce) reduce 方法用于将 Stream 中的元素进行累积操作,通过一个 BinaryOperator 来定义累积的逻辑。它有不同的重载形式,可以提供一个初始值。
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class StreamReduce {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
// 无初始值的归约
Optional<Integer> sumWithoutIdentity = numbers.stream()
.reduce((a, b) -> a + b);
sumWithoutIdentity.ifPresent(System.out::println); // 输出: 6
// 有初始值的归约
int sumWithIdentity = numbers.stream()
.reduce(10, (a, b) -> a + b);
System.out.println(sumWithIdentity); // 输出: 16
}
}
- 查找与匹配
- anyMatch:判断 Stream 中是否至少有一个元素匹配给定的 Predicate。
- allMatch:判断 Stream 中的所有元素是否都匹配给定的 Predicate。
- noneMatch:判断 Stream 中是否没有元素匹配给定的 Predicate。
- findFirst:返回 Stream 中的第一个元素,如果 Stream 为空则返回 Optional.empty。
- findAny:返回 Stream 中的任意一个元素,如果 Stream 为空则返回 Optional.empty,在并行流中表现更高效。
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class StreamFindAndMatch {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
boolean anyMatch = numbers.stream()
.anyMatch(n -> n % 2 == 0);
boolean allMatch = numbers.stream()
.allMatch(n -> n > 0);
boolean noneMatch = numbers.stream()
.noneMatch(n -> n < 0);
Optional<Integer> first = numbers.stream()
.findFirst();
Optional<Integer> any = numbers.stream()
.findAny();
}
}
- 计数 (count) count 方法用于统计 Stream 中的元素个数,返回一个 long 类型的值。
import java.util.ArrayList;
import java.util.List;
public class StreamCount {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
long count = numbers.stream()
.count();
System.out.println(count); // 输出: 3
}
}
- 最大值与最小值 (max/min) max 和 min 方法分别用于获取 Stream 中的最大值和最小值,需要传入一个 Comparator 来定义比较逻辑。
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
public class StreamMaxMin {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
Optional<Integer> max = numbers.stream()
.max(Comparator.naturalOrder());
Optional<Integer> min = numbers.stream()
.min(Comparator.naturalOrder());
}
}
多方法组合处理数据
- 复杂过滤与映射组合 假设我们有一个包含人员信息的列表,每个人员有姓名、年龄和性别。我们想筛选出年龄大于 18 岁的女性,并将她们的姓名转换为大写。
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
class Person {
private String name;
private int age;
private String gender;
public Person(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String getGender() {
return gender;
}
}
public class ComplexStreamOperations {
public static void main(String[] args) {
List<Person> people = new ArrayList<>();
people.add(new Person("Alice", 20, "Female"));
people.add(new Person("Bob", 15, "Male"));
people.add(new Person("Eve", 25, "Female"));
List<String> filteredAndMappedNames = people.stream()
.filter(p -> p.getAge() > 18 && "Female".equals(p.getGender()))
.map(Person::getName)
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(filteredAndMappedNames); // 输出: [ALICE, EVE]
}
}
- 排序、过滤与收集组合 对于一个包含整数的列表,我们先对其进行排序,然后过滤掉小于 5 的数,最后收集到一个新的列表中。
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class SortFilterCollect {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(3);
numbers.add(7);
numbers.add(2);
numbers.add(8);
List<Integer> result = numbers.stream()
.sorted()
.filter(n -> n >= 5)
.collect(Collectors.toList());
System.out.println(result); // 输出: [7, 8]
}
}
- 扁平映射、过滤与归约组合 假设有一个二维数组,我们先将其扁平化为一维 Stream,然后过滤掉负数,最后计算剩余数字的总和。
import java.util.Arrays;
import java.util.stream.IntStream;
public class FlatMapFilterReduce {
public static void main(String[] args) {
int[][] arrays = {{1, -2, 3}, {-4, 5, 6}};
int sum = Arrays.stream(arrays)
.flatMapToInt(IntStream::of)
.filter(n -> n > 0)
.reduce(0, (a, b) -> a + b);
System.out.println(sum); // 输出: 15
}
}
并行流的多方法组合
并行流利用多核处理器的优势,提高大数据量处理的效率。在进行多方法组合时,并行流的中间操作和终端操作与顺序流类似,但执行方式不同。
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class ParallelStreamCombination {
public static void main(String[] args) {
List<Integer> largeList = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
largeList.add(i);
}
List<Integer> result = largeList.parallelStream()
.filter(n -> n % 2 == 0)
.map(n -> n * 2)
.sorted()
.collect(Collectors.toList());
}
}
在上述示例中,我们将一个包含一百万个整数的列表转换为并行流,然后依次进行过滤、映射和排序操作,最后收集结果。并行流会自动将数据分成多个部分,在不同的线程中并行处理这些操作,从而提高整体处理速度。
然而,使用并行流时需要注意一些问题。例如,某些操作在并行流中可能会有性能损耗,如状态相关的操作(如使用有状态的 Collector)。此外,并行流依赖于 Fork/Join 框架,任务划分和合并的开销也需要考虑。在实际应用中,需要根据数据量和操作的复杂度来权衡是否使用并行流。
总结 Stream 多方法组合的优势
- 代码简洁性:通过链式调用多个方法,避免了复杂的循环嵌套和临时变量的声明,使代码更易读和维护。
- 功能强大:可以组合多种中间操作和终端操作,实现复杂的数据处理逻辑,如数据清洗、转换、聚合等。
- 并行处理:支持并行流,能够充分利用多核处理器的性能,提高大数据量处理的效率。
- 声明式编程:Stream API 采用声明式编程风格,只需描述要做什么,而不是如何做,让开发者更专注于业务逻辑。
在实际的 Java 开发中,Stream 多方法组合是处理集合数据的强大工具,无论是在数据处理、数据分析还是其他相关领域,都能发挥重要作用,提升代码的质量和性能。