Stream流

发布于:2024-10-13 ⋅ 阅读:(12) ⋅ 点赞:(0)

目录

Stream 流是什么

Stream 流的作用

如何获取 Stream 对象

下面是一些基本的 Stream 操作示例:

终结方法:

1.void forEach(Consumer action); 遍历流的元素

2.Optional max(比较器); //获取流中元素的最大值,但是必须传入比较器

3.long count(); 获取流中元素的个数

中间方法:

1.Stream  filter( Predicate pre );

2.Stream  map(Function mapper);

3.Stream sorted(比较器); // 传入比较器排序

4.static Stream concat(Stream s1 , Stream s2);

 Stream流的注意事项


Stream 流是什么

在Java中,Stream 是 Java 8 引入的一个重要特性,它是对集合数据进行操作的一种新的方式。使用 Stream 可以让你写出更加简洁、高效的代码。它支持各种包括过滤、映射、汇总等在内的操作,可以链式调用,使得数据处理变得更加灵活。

Stream 流的作用

  • 数据处理:提供了一种高效并且灵活的方式来处理数据。
  • 延迟执行:某些操作(如 filter)不会立即执行,直到遇到终止操作(如 collect 或 forEach)才会执行。
  • 并行操作:可以很容易地创建并行流来利用多核处理器的性能。

如何获取 Stream 对象

获取 Stream 对象的主要方式有以下几种:

  1. 从 Collection 获取:任何实现了 Iterable 接口的对象都可以通过 stream() 方法来创建一个串行流,或者通过 parallelStream() 方法创建一个并行流。
  2. 数组转 Stream:可以通过 Arrays.stream(array) 方法将数组转换成 Stream。
  3. 创建无限流:可以使用 Stream.iterate() 或者 Stream.generate() 创建无限流。
  4. 从特定类的方法中获取:例如 Files.lines(path) 可以从文件中读取每一行生成一个 Stream。 

下面是一些基本的 Stream 操作示例:

终结方法:

方法调用完毕后返回的不在是一个流的对象, 不能继续在使用Stream的功能

1.void forEach(Consumer<? super T> action); 遍历流的元素
 list.stream().forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });
2.Optional max(比较器); //获取流中元素的最大值,但是必须传入比较器
String s1 = list.stream().max((o1, o2) -> o2.length() - o1.length()).get();
3.long count(); 获取流中元素的个数
long count = list.stream().count();

中间方法:

方法调用完毕后返回的依然是一个流的对象, 可以继续使用Stream的功能

1.Stream<T>  filter( Predicate<T> pre );

假设有一个 List<String> 并且你想保留其中长度大于3的字符串:

import java.util.Arrays;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Tom", "Jerry", "Tuffy", "Spike");

        // 使用 filter 方法过滤长度大于3的字符串
        names.stream()
            .filter(name -> name.length() > 3)
            .forEach(System.out::println);
    }
}
2.Stream<T>  map(Function<? super T, ? extends R> mapper);

假设你想将上述列表中的每个元素转换为大写:

// 继续上面的例子
names.stream()
    .map(String::toUpperCase)
    .forEach(System.out::println);
3.Stream<T> sorted(比较器); // 传入比较器排序

如果你想要根据名字的长度来排序:

// 继续上面的例子
names.stream()
    .sorted(Comparator.comparingInt(String::length))
    .forEach(System.out::println);
4.static Stream<T> concat(Stream<T> s1 , Stream<T> s2);
ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "张三丰丰", "张无忌", "周芷若", "赵敏", "张强");
       Stream.concat( list.stream().filter(s -> s.startsWith("张")),list.stream().filter(s -> s.length() == 3) )
                .forEach(s-> System.out.println(s));

 Stream流的注意事项

当使用 Java 中的 Stream API 时,有一些需要注意的事项可以帮助你更好地理解和使用它们:

  1. 惰性求值:大部分中间操作(如 filter, map, flatMap 等)都是惰性的,它们并不会立即执行,只有在触发终端操作(如 forEach, collect, reduce 等)时,中间操作才会被执行。

  2. 管道化:一旦定义了一个流,就可以在一个表达式中链接多个操作,形成一个处理管道。这使得代码更易于阅读和维护。

  3. 不可变性:流本身是不可变的,一旦创建就不能改变其状态。所有的操作都会返回新的流。

  4. 一次性:除非明确指定(如通过 peek),否则每个流只能被“消费”一次。一旦执行了终端操作,流就无法再次被使用。

  5. 并行流:虽然并行流可以提高执行效率,但它并不是所有场景下的最佳选择。并行流消耗更多的系统资源,并且对于涉及大量同步或阻塞操作的情况可能没有帮助。

  6. 短路行为:一些终端操作,比如 findFirst, anyMatch, noneMatch 在满足条件时会提前终止,这种短路行为可以节省不必要的计算。

  7. 数据源类型:创建流时要考虑到数据源的类型。对于有限的数据源(如集合),可以直接创建流;而对于无限的数据源(如迭代器产生的数据),需要谨慎处理避免无限循环。

  8. 性能考虑:尽管 Stream 提供了更简洁的语法,但在某些情况下,传统的循环可能更快。尤其是当你处理大数据集时,要评估性能影响。

  9. 异常处理:流的操作可能会抛出异常,特别是在使用自定义函数作为参数时。需要确保正确处理这些异常。

  10. 线程安全:并行流在多线程环境中运行,因此需要考虑线程安全问题,尤其是在使用共享资源时。