一 stream的介绍
在Java中,Stream是一种用于对集合数据进行操作的API。它提供了一种函数式编程的方式来处理集合,使得代码更加简洁、优雅且易于并行化处理。Stream API允许你通过一系列的中间操作和终端操作来对集合数据进行处理。
二 基本使用
1.创建Stream: 可以通过Collection
接口的stream()
方法创建一个Stream,或者通过Stream
类的静态方法of()
、generate()
、iterate()
等创建Stream。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream1 = numbers.stream();
Stream<Integer> stream2 = Stream.of(1, 2, 3, 4, 5);
Stream<Integer> stream3 = Stream.generate(() -> 1).limit(5);
Stream<Integer> stream4 = Stream.iterate(1, n -> n + 1).limit(5);
2.中间操作: 中间操作是指返回一个新Stream的操作,常用的中间操作包括filter()
、map()
、sorted()
、distinct()
、limit()
等。
filter(Predicate<T> predicate)
: 过滤Stream中不符合条件的元素,并返回一个新的Stream。List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); Stream<Integer> filteredStream = numbers.stream() .filter(n -> n % 2 == 0); // 使用forEach终端操作打印过滤后的结果 filteredStream.forEach(System.out::println); // 输出结果:2 4
map(Function<T, R> mapper)
: 对Stream中的元素进行映射转换,并返回一个新的Stream。List<String> names = Arrays.asList("Alice", "Bob", "Charlie"); Stream<String> mappedStream = names.stream() .map(name -> "Hello, " + name); // 使用forEach终端操作打印映射后的结果 mappedStream.forEach(System.out::println); // 输出结果:Hello, Alice Hello, Bob Hello, Charlie
sorted(Comparator<T> comparator)
: 对Stream中的元素进行排序,并返回一个新的Stream。List<Integer> numbers = Arrays.asList(3, 1, 5, 2, 4); Stream<Integer> sortedStream = numbers.stream() .sorted(); // 使用forEach终端操作打印排序后的结果 sortedStream.forEach(System.out::println); // 输出结果:1 2 3 4 5
distinct()
: 去除Stream中重复的元素,并返回一个新的Stream。List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 3); Stream<Integer> distinctStream = numbers.stream() .distinct(); // 使用forEach终端操作打印去重后的结果 distinctStream.forEach(System.out::println); // 输出结果:1 2 3
limit(long maxSize)
: 截取Stream中的前n个元素,并返回一个新的Stream。List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); Stream<Integer> limitedStream = numbers.stream() .limit(3); // 使用forEach终端操作打印截取后的结果 limitedStream.forEach(System.out::println); // 输出结果:1 2 3
3.终端操作: 终端操作是指返回一个非Stream的结果的操作,用于触发Stream的计算。常用的终端操作包括forEach()
、collect()
、reduce()
、min()
、max()
、count()
等。
forEach(Consumer<T> action)
: 对Stream中的每个元素执行指定操作,没有返回值。List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); numbers.stream() .forEach(System.out::println); // 输出结果:1 2 3 4 5
collect(Collector<T, A, R> collector)
: 对Stream中的元素进行汇总操作,返回一个结果集合。List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); List<Integer> collectedList = numbers.stream() .filter(n -> n % 2 == 0) .collect(Collectors.toList()); System.out.println(collectedList); // 输出结果:[2, 4]
reduce(BinaryOperator<T> accumulator)
: 对Stream中的元素进行聚合操作,返回一个Optional结果。List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); Optional<Integer> sum = numbers.stream() .reduce(Integer::sum); System.out.println(sum); // 输出结果:Optional[15]
min(Comparator<T> comparator)
: 找到Stream中的最小元素,返回一个Optional结果。List<Integer> numbers = Arrays.asList(3, 1, 5, 2, 4); Optional<Integer> min = numbers.stream() .min(Integer::compare); System.out.println(min); // 输出结果:Optional[1]
max(Comparator<T> comparator)
: 找到Stream中的最大元素,返回一个Optional结果。List<Integer> numbers = Arrays.asList(3, 1, 5, 2, 4); Optional<Integer> max = numbers.stream() .max(Integer::compare); System.out.println(max); // 输出结果:Optional[5]
count()
: 统计Stream中的元素数量,返回一个long类型的结果。List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); long count = numbers.stream() .count(); System.out.println(count); // 输出结果:5
4.并行处理: Stream API支持并行处理,可以通过parallel()
方法将Stream转换为并行Stream,以提高处理速度。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> parallelStream = numbers.parallelStream();
parallelStream.forEach(System.out::println);
以上是Java中Stream的一些常用操作,通过使用Stream API,你可以更加方便地对集合数据进行处理,并且代码会更加简洁和易于理解。需要注意的是,Stream是一次性的,一旦触发终端操作,Stream将会被关闭,不能再次使用。
三 扩展与优化
- 使用Lambda表达式和方法引用: 在中间操作和终端操作中,使用Lambda表达式和方法引用可以使代码更加简洁和易于理解。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); // 使用Lambda表达式 List<Integer> filteredList = numbers.stream() .filter(n -> n % 2 == 0) .collect(Collectors.toList()); // 使用方法引用 List<String> mappedList = filteredList.stream() .map(n -> "Number: " + n) .collect(Collectors.toList());
- 使用
orElse
或orElseGet
: 在使用Optional
时,可以使用orElse
或orElseGet
来提供默认值,避免返回null。
Optional<Integer> optionalValue = numbers.stream() .filter(n -> n > 10) .findFirst(); // 使用orElse Integer value = optionalValue.orElse(-1); // 使用orElseGet Integer value = optionalValue.orElseGet(() -> expensiveOperation());
- 使用
findFirst
或findAny
: 在找到第一个元素时,使用findFirst
,如果仅需要一个元素而不关心具体是哪个元素,可以使用findAny
。在并行处理时,findAny
性能更好。Optional<Integer> firstValue = numbers.stream() .findFirst(); Optional<Integer> anyValue = numbers.parallelStream() .findAny();
- 避免不必要的中间操作: Stream的中间操作是惰性求值的,只有在终端操作时才会触发计算。因此,应尽量避免不必要的中间操作,以提高性能。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); // 不必要的中间操作 List<Integer> filteredList = numbers.stream() .filter(n -> n % 2 == 0) .map(n -> n * 2) .collect(Collectors.toList()); // 优化后的操作 List<Integer> optimizedList = numbers.stream() .filter(n -> n % 2 == 0) .collect(Collectors.toList());
- 使用
flatMap
: 当处理嵌套集合时,可以使用flatMap
将嵌套集合展平成一个新的Stream。List<List<Integer>> nestedList = Arrays.asList( Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6), Arrays.asList(7, 8, 9) ); List<Integer> flattenedList = nestedList.stream() .flatMap(Collection::stream) .collect(Collectors.toList());
- 使用并行Stream: 在大规模数据处理时,可以使用并行Stream来提高处理速度。但是需要注意,不是所有的场景都适合使用并行Stream,因为并行处理可能引发线程安全问题。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); // 串行处理 numbers.stream() .forEach(System.out::println); // 并行处理 numbers.parallelStream() .forEach(System.out::println);
总之,使用Stream API可以使代码更加简洁、易读和高效。在扩展和优化Stream操作时,需要根据具体场景合理选择不同的操作方法,以满足需求并提升性能。同时,遵循函数式编程的原则,避免副作用和共享状态,可以更好地利用Stream API的优势。