Java Stream MD笔记
一、Java Stream概念
Java Stream是Java 8引入的一种新的数据处理方式,提供了一种高效、便利的方法来处理集合数据。Stream流可以让开发人员以声明式的方式对数据进行操作,从而使代码更加简洁、易读。Stream本身不存储数据,而是通过管道传输数据,支持类似于SQL语句的操作,如过滤、映射、排序等。
二、Stream的特点
1. **不存储数据**:Stream操作是延迟执行的,只有在终端操作时才会触发实际的计算。
2. **不可变性**:Stream操作不会改变源对象,而是返回一个新的Stream。
3. **支持并行操作**:通过`parallelStream()`方法,可以将流转换为并行流,利用多核处理器提高处理性能。
4. **操作多样性**:提供了丰富的中间操作和终端操作,如`filter()`, `map()`, `sorted()`, `collect()`等。
三、Stream的创建
1. 通过集合创建
List<Integer> list = Arrays.asList(5, 2, 3, 1, 4);
Stream<Integer> stream = list.stream();
2. 通过数组创建
String[] array = {"ab", "abc", "abcd", "abcde", "abcdef"};
Stream<String> stream = Arrays.stream(array);
3. 使用Stream静态方法
Stream<String> stream = Stream.of("ab", "abc", "abcd", "abcde", "abcdef");
Stream<Integer> stream2 = Stream.iterate(0, x -> x + 3).limit(5);
Stream<Integer> stream3 = Stream.generate(() -> new Random().nextInt()).limit(3);
4. 通过IntStream、LongStream、DoubleStream接口
IntStream intStream = IntStream.of(1, 2, 3, 4, 5);
IntStream range = IntStream.range(1, 5);
IntStream rangeClosed = IntStream.rangeClosed(1, 5);
四、Stream的中间操作
1. 过滤(filter)
Stream<String> stringStream = Stream.of("111", "22", "33333", "4444", "5555555", "111");
Stream<String> filteredStream = stringStream.filter(o -> o.length() < 3);
2. 截取(limit)
Stream<String> limitedStream = stringStream.limit(2);
3. 跳过(skip)
Stream<String> skippedStream = stringStream.skip(2).limit(2);
4. 去重(distinct)
Stream<String> distinctStream = stringStream.distinct();
5. 排序(sorted)
Stream<String> sortedStream = stringStream.sorted((o1, o2) -> o2.length() - o1.length());
6. 映射(map)
Stream<Integer> mappedStream = stringStream.map(Integer::parseInt);
五、Stream的终端操作
1. 最小值(min)和最大值(max)
Optional<Integer> min = mappedStream.distinct().min(Integer::compare);
Optional<Integer> max = mappedStream.distinct().max(Integer::compare);
2. 计数(count)
long count = stringStream.count();
3. 归约(reduce)
int sum = mappedStream.distinct().reduce(0, Integer::sum);
4. 收集(collect)
List<String> collectedList = stringStream.collect(Collectors.toList());
六、Stream的并行操作
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
int parallelSum = numbers.parallelStream().reduce(0, Integer::sum);
七、Stream与集合的比较
- 简洁性:Stream流以声明式的方式操作数据,代码更加简洁、易读。
- 延迟执行:Stream流的操作是延迟执行的,提高了效率
当然,很高兴能够继续扩展这篇关于Java Stream的MD笔记。下面,我将增加一些高级话题和实际应用场景,以便更全面地了解Java Stream的强大功能。
八、Stream的高级特性
1. 条件性操作(peek)
peek
方法是一个中间操作,它接收一个Consumer函数式接口作为参数,对Stream中的每个元素执行操作,但不影响Stream本身。主要用于调试或日志记录等场景。
Stream<String> stream = Stream.of("a", "bb", "ccc", "dddd");
stream.peek(System.out::println).filter(s -> s.length() > 1).forEach(System.out::println);
// 输出:
// a
// bb
// ccc
// dddd
// bb
// ccc
// dddd
注意:虽然peek
可以输出元素,但上面的例子中,原始流中的"a"也被输出了,因为它在过滤之前就被处理了。
2. 自定义收集器(Collectors)
Java Stream API提供了Collectors
类,用于实现各种终端操作,如收集到列表、集合、映射等。但你也可以通过Collectors.collectingAndThen()
或Collectors.toMap()
等方法的重载版本,以及自定义Collector来实现更复杂的收集逻辑。
Map<Boolean, List<String>> partition = stream.collect(Collectors.partitioningBy(s -> s.length() > 2));// 自定义Collector示例
Collector<String, ?, String> joiningCollector = Collectors.collectingAndThen(Collectors.toList(),list -> String.join(",", list)
);
String joined = stream.collect(joiningCollector);
3. 扁平化流(flatMap)
当你有一个元素本身是流(或可以转换为流)的流时,flatMap
方法非常有用。它将每个子流中的元素“扁平化”到主流中,从而得到一个包含所有子流元素的单一流。
List<List<String>> listOfLists = Arrays.asList(Arrays.asList("a", "b"),Arrays.asList("c", "d"),Arrays.asList("e")
);
Stream<String> flatStream = listOfLists.stream().flatMap(Collection::stream);
flatStream.forEach(System.out::println);
// 输出:
// a
// b
// c
// d
// e
九、Stream在实际应用中的案例
1. 文本处理
假设你有一个文件,包含多行文本,每行文本都是JSON格式的字符串。你可以使用Stream来读取文件,每行作为一个元素,然后解析JSON并处理数据。
Files.lines(Paths.get("data.txt")).map(jsonLine -> new ObjectMapper().readValue(jsonLine, MyObject.class)).forEach(obj -> processObject(obj));
2. 复杂的数据转换
当你需要从一组复杂对象中提取特定信息,并将这些信息转换为另一种格式时,Stream API提供了强大的支持。
List<ComplexObject> complexObjects = ...;
List<String> result = complexObjects.stream().map(obj -> obj.getNestedObject().getField()).filter(field -> !field.isEmpty()).collect(Collectors.toList());
3. 并发数据处理
Stream API支持并行流,可以利用多核处理器的优势来加速数据处理。但需要注意的是,并行流并不总是比顺序流更快,其性能取决于数据的特性和操作的复杂度。
IntStream.range(0, 1000).parallel().map(i -> complexCalculation(i)).sum();
十、注意事项和最佳实践
-
性能考量:并行流不总是比顺序流快,特别是在数据量不大或操作不复杂时。此外,并行流可能会引入额外的线程开销和竞争条件。
-
中间操作与终端操作:确保你的Stream链以终端操作结束,否则Stream将不会执行任何操作。
-
状态管理:在Stream操作中避免使用可变状态,因为这可能会导致不可预测的结果。
-
错误处理:Stream API中的错误处理通常是通过异常来实现的。确保你的代码能够妥善处理可能抛出的异常。
-
可读性:虽然Stream API可以使代码更简洁,但过度使用或不恰当的使用可能会降低代码的可读性。保持代码的清晰和易于理解是很重要的。