凡科登陆网站手机版,专门教做衣服的网站,常州城投建设招标网站,九江网站设计公司深入理解Java Stream#xff1a;优雅而强大的数据处理
在Java编程世界中#xff0c;数据处理是一个不可避免的任务。为了更高效、更简洁地处理数据#xff0c;Java 8引入了Stream API。Stream API 提供了一种新的抽象#xff0c;使得我们可以以一种更函数式的方式处理集合…深入理解Java Stream优雅而强大的数据处理
在Java编程世界中数据处理是一个不可避免的任务。为了更高效、更简洁地处理数据Java 8引入了Stream API。Stream API 提供了一种新的抽象使得我们可以以一种更函数式的方式处理集合数据。在本文中我们将深入探讨Java Stream的一些常用方法以及它们如何使得数据处理变得更加优雅和强大。
什么是Java Stream
Java Stream是一种用于处理集合数据的API它引入了一种新的抽象让我们能够以声明式的方式处理数据。与传统的集合操作方式相比Stream API提供了更高层次、更函数式的操作。
基础概念
在开始介绍常用方法之前让我们先了解一些Java Stream的基础概念。
流的创建
要使用流首先需要从一个数据源创建它。常见的数据源包括集合、数组、I/O通道等。下面是一些创建流的方式
ListString myList Arrays.asList(apple, orange, banana);
StreamString streamFromList myList.stream();int[] array {1, 2, 3, 4, 5};
IntStream streamFromArray Arrays.stream(array);中间操作和终端操作
流的操作可以分为中间操作和终端操作。中间操作返回一个新的流可以通过链式调用多个中间操作。终端操作触发流的遍历并生成最终的结果。
ListString result myList.stream().filter(s - s.startsWith(a)).map(String::toUpperCase).collect(Collectors.toList());上述代码中filter 和 map 是中间操作而 collect 是终端操作。
常用方法
1. filter
filter 方法用于过滤流中的元素接受一个Predicate作为参数返回一个新的流。
ListString filteredList myList.stream().filter(s - s.length() 5).collect(Collectors.toList());2. map
map 方法用于对流中的每个元素应用一个函数将其映射成一个新的元素。
ListInteger lengths myList.stream().map(String::length).collect(Collectors.toList());3. forEach
forEach 方法对流中的每个元素执行指定的操作通常用于遍历流。
myList.stream().forEach(System.out::println);4. collect
collect 方法是一个终端操作将流中的元素收集到一个结果容器中比如List、Set或Map。
ListString collectedList myList.stream().filter(s - s.length() 5).collect(Collectors.toList());5. reduce
reduce 方法可以将流中的元素组合起来产生一个新的值。它接受一个初始值和一个BinaryOperator。
OptionalString concatenated myList.stream().reduce((s1, s2) - s1 s2);当使用Java Stream进行数据处理时除了上述提到的常用方法外还有一些其他有趣和强大的方法让我们继续深入探讨。
6. flatMap
flatMap 方法用于将一个流中的每个元素都转换为另一个流然后将这些流连接起来。它常用于处理嵌套的集合结构。
ListListString nestedList Arrays.asList(Arrays.asList(apple, banana),Arrays.asList(orange, grape),Arrays.asList(melon, peach)
);ListString flatMapResult nestedList.stream().flatMap(Collection::stream).collect(Collectors.toList());7. distinct
distinct 方法用于去除流中重复的元素根据元素的自然顺序或者通过自定义的比较器来进行判定。
ListString distinctList myList.stream().distinct().collect(Collectors.toList());8. sorted
sorted 方法用于对流中的元素进行排序。可以使用自然排序或者通过传递一个自定义的比较器。
ListString sortedList myList.stream().sorted().collect(Collectors.toList());9. limit 和 skip
limit 方法用于截取流中的前 N 个元素而 skip 方法则用于跳过流中的前 N 个元素。
ListString limitedList myList.stream().limit(3).collect(Collectors.toList());ListString skippedList myList.stream().skip(2).collect(Collectors.toList());10. anyMatch、allMatch 和 noneMatch
这些方法用于检查流中的元素是否满足某个条件。anyMatch 判断是否至少有一个元素满足条件allMatch 判断是否所有元素都满足条件而 noneMatch 判断是否所有元素都不满足条件。
boolean anyMatchResult myList.stream().anyMatch(s - s.startsWith(a));boolean allMatchResult myList.stream().allMatch(s - s.length() 3);boolean noneMatchResult myList.stream().noneMatch(s - s.contains(z));1. 自定义操作符
Java Stream API允许我们自定义操作符以满足特定的需求。通过 map 或 flatMap 结合自定义的函数可以创建强大的操作符。
例如假设我们想要一个操作符将每个字符串转换为它的首字母并返回一个新的流
ListCharacter firstLetters myList.stream().map(s - s.charAt(0)).collect(Collectors.toList());12. joining
joining 是一个终端操作用于连接流中的元素。这在处理字符串时特别有用。
String result myList.stream().collect(Collectors.joining(, ));上述代码将流中的字符串用逗号和空格连接成一个字符串。
13. groupingBy 和 partitioningBy
groupingBy 方法用于按照某个条件对流中的元素进行分组返回一个 Map。而 partitioningBy 则是 groupingBy 的一种特殊情况根据条件将流分为两组。
MapInteger, ListString groupedByLength myList.stream().collect(Collectors.groupingBy(String::length));MapBoolean, ListString partitioned myList.stream().collect(Collectors.partitioningBy(s - s.length() 3));14. 并行流
Java Stream 还提供了并行流的支持充分发挥多核处理器的优势加速数据处理。只需在流的创建过程中调用 parallel() 方法即可将流转换为并行流。
ListString parallelResult myList.parallelStream().filter(s - s.length() 5).collect(Collectors.toList());15. 异步操作
通过 CompletableFuture 结合 supplyAsync 方法我们可以异步处理流中的元素提高程序的性能。
CompletableFutureListString futureResult CompletableFuture.supplyAsync(() -myList.stream().filter(s - s.length() 5).collect(Collectors.toList())
);16. reduce 的更多应用
reduce 方法除了用于组合元素外还可以用于执行更复杂的归约操作。例如计算集合中所有字符串的总长度
int totalLength myList.stream().map(String::length).reduce(0, Integer::sum);上述代码中map 将每个字符串映射为它的长度然后 reduce 方法对这些长度进行求和。
17. findFirst 和 findAny
findFirst 用于找到流中的第一个元素而 findAny 则返回流中的任意一个元素。这在需要获取满足条件的第一个元素时非常有用。
OptionalString firstElement myList.stream().findFirst();OptionalString anyElement myList.stream().findAny();18. peek
peek 方法用于在流的每个元素执行操作时生成一个新的流通常用于调试和理解流的中间操作过程。
ListString peekedValues myList.stream().peek(s - System.out.println(Processing element: s)).collect(Collectors.toList());19. 自定义收集器
除了提供的预定义收集器外Java Stream API还允许我们创建自定义的收集器以满足特定的需求。这需要实现 Collector 接口的三个方法supplier、accumulator 和 combiner。
20. 异常处理
在流的处理过程中如果希望捕获并处理异常可以使用 try-catch 块或者 exceptionally 方法。
ListString resultList myList.stream().map(s - {try {return someMethodThatThrowsException(s);} catch (Exception e) {// Handle exceptionreturn defaultValue;}}).collect(Collectors.toList());21. 自定义过滤器
除了内置的 filter 方法外我们还可以创建自定义的过滤器以便更灵活地处理流中的元素。这可以通过实现 Predicate 接口来实现。
public class CustomFilter implements PredicateString {Overridepublic boolean test(String s) {// 自定义过滤逻辑return s ! null s.length() 3;}
}// 使用自定义过滤器
ListString customFilteredList myList.stream().filter(new CustomFilter()).collect(Collectors.toList());这样我们可以根据项目的需求轻松创建各种自定义的过滤器。
22. 处理空值
在实际应用中我们经常需要处理可能为空的数据。Java Stream 提供了 Optional 类型使得我们能够更好地处理可能为空的元素。
ListString nonNullValues myList.stream().map(Optional::ofNullable).flatMap(Optional::stream).collect(Collectors.toList());在这个例子中ofNullable 方法将元素包装成 Optional然后通过 flatMap 过滤掉空值。
23. 并行流的注意事项
虽然并行流能够加速处理但在使用时需要注意共享状态和线程安全的问题。确保对共享变量的修改是线程安全的以避免潜在的并发问题。
24. 使用 Stream 构建器
Java 9 引入了 Stream.Builder 接口使得我们可以更方便地构建流特别是在需要动态添加元素的情况下。
Stream.BuilderString builder Stream.builder();
builder.accept(apple);
builder.accept(orange);
builder.accept(banana);StreamString fruits builder.build();25. 组合多个流
Stream.concat 方法允许我们将两个流合并成一个流。 StreamString combinedStream Stream.concat(stream1, stream2);这可以用于组合多个数据源或者处理多个流的情况。
26. 时间和日期处理
在Java 8之后引入了LocalDate、LocalTime、LocalDateTime等新的日期时间类结合Java Stream我们可以更便捷地处理时间序列数据。
ListLocalDate dateList Arrays.asList(LocalDate.of(2023, 1, 1),LocalDate.of(2023, 2, 1),LocalDate.of(2023, 3, 1)
);ListLocalDate filteredDates dateList.stream().filter(date - date.isAfter(LocalDate.now())).collect(Collectors.toList());27. 收集统计信息
Java Stream API提供了Collectors类其中包含了一些有用的收集器。例如Collectors.summarizingInt可以用于汇总统计信息如最大值、最小值、平均值和总和。
ListInteger numbers Arrays.asList(1, 2, 3, 4, 5);IntSummaryStatistics stats numbers.stream().collect(Collectors.summarizingInt(Integer::intValue));System.out.println(Max: stats.getMax());
System.out.println(Min: stats.getMin());
System.out.println(Average: stats.getAverage());
System.out.println(Sum: stats.getSum());28. 与 Map 的结合运用
结合Map的功能我们可以更灵活地对流进行分组、分区或者进行其他复杂的操作。
MapInteger, ListString lengthGroup myList.stream().collect(Collectors.groupingBy(String::length));MapBoolean, ListString partitionedMap myList.stream().collect(Collectors.partitioningBy(s - s.length() 3));29. 自定义排序
除了 sorted 方法提供的排序方式外我们还可以使用 Comparator 接口自定义排序规则。
ListString sortedByLength myList.stream().sorted(Comparator.comparingInt(String::length)).collect(Collectors.toList());30. Stream API 的延迟执行
Stream API支持延迟执行只有在终端操作被调用时中间操作才会开始执行。这种特性允许我们构建更为高效的数据处理流程。
StreamString lazyStream myList.stream().filter(s - s.length() 3).map(String::toUpperCase);// 终端操作触发中间操作的执行
ListString result lazyStream.collect(Collectors.toList());31. 文件处理与IO操作
Java Stream API也可以与文件IO结合使用提供了便捷的方式来处理文件中的数据。例如我们可以通过Files.lines方法获取文件中的所有行并进行进一步的处理
try {StreamString lines Files.lines(Paths.get(example.txt));ListString filteredLines lines.filter(line - line.contains(Java)).collect(Collectors.toList());lines.close();
} catch (IOException e) {e.printStackTrace();
}32. 自定义收集器进阶
除了常见的收集器外我们还可以通过Collector.of方法创建自定义的收集器以满足更灵活的需求。这种方式可以用于实现一些特定的数据结构或者聚合操作。
CollectorMyObject, StringBuilder, String myCollector Collector.of(StringBuilder::new,(result, element) - result.append(element.getName()).append(, ),StringBuilder::append,StringBuilder::toString);String result myList.stream().collect(myCollector);33. 操作流的元数据
通过count、anyMatch、allMatch等方法我们可以获取关于流的元数据信息例如流中元素的数量、是否存在满足某条件的元素等。
long count myList.stream().count();boolean anyMatch myList.stream().anyMatch(s - s.startsWith(a));boolean allMatch myList.stream().allMatch(s - s.length() 2);34. 自定义并行度
在处理大规模数据时我们可以通过指定自定义的并行度来优化性能。通过parallelStream方法的参数我们可以控制并行度的级别。
ListString parallelResult myList.parallelStream().withParallelism(4) // 自定义并行度.filter(s - s.length() 5).collect(Collectors.toList());35. 多级分组和多级映射
Collectors.groupingBy 方法支持多级分组而 Collectors.mapping 方法支持多级映射。这使得我们可以更复杂地组织和处理数据。
MapInteger, MapCharacter, ListString groupedByLengthAndFirstChar myList.stream().collect(Collectors.groupingBy(String::length,Collectors.groupingBy(s - s.charAt(0))));36. 使用 Collectors.toMap 进行自定义映射
在收集流元素到Map时Collectors.toMap 允许我们指定键和值的映射关系。这对于从流中构建自定义的映射结构非常有用。
MapInteger, String lengthToNameMap myList.stream().collect(Collectors.toMap(String::length, Function.identity()));这里使用了 Function.identity() 作为值映射表示使用元素本身作为值。
37. 创建无限流
Java Stream API允许我们创建无限流这对于模拟数据流或者处理动态生成的数据非常有用。例如生成一个无限递增的整数流 StreamInteger infiniteStream Stream.iterate(0, i - i 1);38. 并行流的线程池定制
通过 ForkJoinPool 可以为并行流提供自定义的线程池以更好地控制并行执行的行为。
ForkJoinPool customThreadPool new ForkJoinPool(4); // 4是并行度
ListString parallelResult myList.parallelStream().withExecutor(customThreadPool).filter(s - s.length() 5).collect(Collectors.toList());39. 使用 Files.walk 处理目录
Files.walk 方法允许我们在目录中递归地处理文件。结合流操作我们可以方便地筛选和处理目录中的文件。
try {StreamPath filePaths Files.walk(Paths.get(directoryPath));ListString fileNames filePaths.filter(Files::isRegularFile).map(Path::getFileName).map(Path::toString).collect(Collectors.toList());filePaths.close();
} catch (IOException e) {e.printStackTrace();
}40. 使用 IntStream、DoubleStream 和 LongStream
除了通用的 Stream 接口Java Stream API还提供了专门用于处理原始数据类型的流。这可以提高性能并减少装箱拆箱的开销。
IntStream.rangeClosed(1, 5).forEach(System.out::println);DoubleStream.of(1.0, 2.0, 3.0).map(d - d * 2).forEach(System.out::println);41. 使用 flatMap 处理嵌套结构
flatMap 不仅用于处理集合类型还可以处理嵌套结构例如列表中包含了另一层的列表。这时flatMap 可以将多层结构扁平化方便后续的操作。
ListListString nestedList Arrays.asList(Arrays.asList(apple, banana),Arrays.asList(orange, grape),Arrays.asList(melon, peach)
);ListString flatMapResult nestedList.stream().flatMap(Collection::stream).collect(Collectors.toList());42. 自定义并发操作
对于并行流我们可以使用 parallel() 方法开启并行处理。但有时我们可能需要更精细的控制并在某一步骤中执行自定义的并发操作。
ListString parallelResult myList.stream().unordered().parallel().filter(s - s.length() 5).collect(Collectors.toList());通过 unordered() 方法我们告诉流操作不依赖元素的顺序这有助于提高并行执行的效率。
43. 使用 Arrays.stream 处理数组
对于数组我们可以使用 Arrays.stream 方法将数组转换为流方便利用流的操作。
int[] array {1, 2, 3, 4, 5};
IntStream streamFromArray Arrays.stream(array);
int sum streamFromArray.sum();44. 操作原始类型的 Optional
Optional 类型不仅可以用于对象类型还可以用于原始类型。例如OptionalInt、OptionalDouble、OptionalLong。
OptionalInt max IntStream.of(1, 2, 3, 4, 5).max();int maxValue max.orElse(0);45. 使用 Stream.generate 创建无限流
Stream.generate 方法允许我们使用提供的 Supplier 生成无限流。这对于需要模拟或生成数据流的情况非常有用。
StreamString randomStrings Stream.generate(() -UUID.randomUUID().toString().substring(0, 8));46. 使用 Stream.iterate 实现斐波那契数列
通过 Stream.iterate 方法我们可以生成无限流。利用这一特性我们可以非常简洁地生成斐波那契数列。
Stream.iterate(new int[]{0, 1}, fib - new int[]{fib[1], fib[0] fib[1]}).limit(10).mapToInt(fib - fib[0]).forEach(System.out::println);47. 自定义收集器进阶应用
在自定义收集器时我们可以实现更复杂的逻辑例如在收集过程中进行聚合或者其他特殊处理。
ListString customizedCollection myList.stream().collect(CustomCollector::new,CustomCollector::accumulate,CustomCollector::combine).finisher();这里 CustomCollector 是一个自定义的收集器。
48. 使用 Stream.concat 合并多个流
Stream.concat 方法可以用于合并两个流。这对于在处理不同来源的数据时能够更为灵活地组合多个流。
StreamString stream1 Stream.of(apple, banana);
StreamString stream2 Stream.of(orange, grape);StreamString combinedStream Stream.concat(stream1, stream2);49. 创建自定义的流源
除了集合、数组、文件等已有的数据源我们还可以通过实现 Spliterator 接口自定义流的源头。
public class CustomSpliterator implements SpliteratorString {// 实现Spliterator接口的方法
}StreamString customStream StreamSupport.stream(new CustomSpliterator(), false);50. 使用 peek 进行调试
peek 方法用于在流的每个元素上执行操作通常用于调试和理解流的中间操作过程。这对于在流处理过程中输出中间结果非常有帮助。
ListString peekedValues myList.stream().peek(s - System.out.println(Processing element: s)).collect(Collectors.toList());51. 使用 Collectors.collectingAndThen 完善收集过程
Collectors.collectingAndThen 方法允许我们在收集完成后应用一些额外的转换。这对于在收集完成后执行最后一步处理非常有用。
ListString result myList.stream().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));上述例子中使用 collectingAndThen 将得到的 List 转为不可修改的列表。
52. 使用 Stream.concat 合并多个流
Stream.concat 方法可以用于合并两个流。这对于在处理不同来源的数据时能够更为灵活地组合多个流。
StreamString stream1 Stream.of(apple, banana);
StreamString stream2 Stream.of(orange, grape);StreamString combinedStream Stream.concat(stream1, stream2);53. 处理多个 Optional
Optional 提供了一些方法可以更方便地处理多个 Optional 对象。
OptionalString optional1 Optional.of(Hello);
OptionalString optional2 Optional.of(World);OptionalString result optional1.flatMap(s1 -optional2.map(s2 - s1 s2));这种方式避免了使用多层嵌套的 ifPresent 或 isPresent 判断。
54. 使用 IntStream 处理日期范围
在处理日期范围时可以使用 IntStream 生成日期的整数表示再转为日期对象。
LocalDate startDate LocalDate.of(2023, 1, 1);
LocalDate endDate LocalDate.of(2023, 12, 31);IntStream.range(0, ChronoUnit.DAYS.between(startDate, endDate)).mapToObj(startDate::plusDays).forEach(System.out::println);55. 处理重复元素
通过 Collectors.toSet 或者 Collectors.toMap 可以轻松去除流中的重复元素。
ListString distinctList myList.stream().distinct().collect(Collectors.toList());56. 使用 Stream.iterate 实现无限递增序列
通过 Stream.iterate 方法我们可以生成无限递增的序列。这对于模拟或生成一系列数字非常有用。
Stream.iterate(0, i - i 2).limit(10).forEach(System.out::println);上述例子中我们生成了一个包含前10个偶数的序列。
57. 使用 Stream.of 处理多个元素
Stream.of 方法可以接受多个元素这样我们可以轻松地创建一个包含多个元素的流。 StreamString stringStream Stream.of(apple, banana, orange);58. 使用 IntStream.range 生成范围内的整数
IntStream.range 方法用于生成一个范围内的整数序列。这对于需要处理一系列整数的情况非常方便。
IntStream.range(1, 6).forEach(System.out::println);上述例子中我们生成了一个包含1到5的整数序列。
59. 使用 Files.lines 读取文件内容
Files.lines 方法可以用于轻松读取文件的内容并将其转化为流。这在处理大型文本文件时非常有用。
try {StreamString fileLines Files.lines(Paths.get(example.txt));fileLines.forEach(System.out::println);fileLines.close();
} catch (IOException e) {e.printStackTrace();
}60. 使用 Collectors.reducing 进行更复杂的归约
Collectors.reducing 方法允许我们进行更复杂的归约操作可以自定义归约的初始值、累加器和组合器。
OptionalString concatenatedString myList.stream().collect(Collectors.reducing((s1, s2) - s1 s2));61. 使用 Stream.generate 生成随机数流
Stream.generate 方法允许我们生成一个无限的随机数流。结合 Random 类我们可以轻松模拟或处理一系列随机数据。
Random random new Random();
StreamInteger randomStream Stream.generate(random::nextInt);62. 使用 Collectors.partitioningBy 分区数据
Collectors.partitioningBy 方法用于将流中的元素根据条件分成两个部分返回一个 MapBoolean, ListT。
MapBoolean, ListString partitionedMap myList.stream().collect(Collectors.partitioningBy(s - s.length() 3));63. 使用 Stream.concat 合并数组流
Stream.concat 不仅可以合并两个流还可以合并数组的流。这对于在处理多个数组时更为方便。
String[] array1 {apple, banana};
String[] array2 {orange, grape};StreamString combinedStream Stream.concat(Arrays.stream(array1), Arrays.stream(array2));64. 使用 IntStream 处理并行计算
IntStream 类提供了一系列用于处理原始 int 类型数据的方法。在并行计算时使用 parallel 方法可以提高性能。
int sum IntStream.rangeClosed(1, 1000).parallel().sum();65. 使用 Stream.iterate 创建斐波那契元组
利用 Stream.iterate 方法我们可以创建一个斐波那契数列的元组流其中每个元组包含相邻的两个斐波那契数。
Stream.iterate(new int[]{0, 1}, fib - new int[]{fib[1], fib[0] fib[1]}).limit(10).forEach(tuple - System.out.println(( tuple[0] , tuple[1] )));通过继续深入学习和实践这些技巧你将更加游刃有余地应对不同的数据处理场景。这些方法和工具的灵活性使得Java Stream API成为处理集合和流数据的强大工具。愿你在使用这些技术的过程中取得更多的成就不断提升自己的编程技能