java8 stream使用教程

简介

Java 8 API添加了一个支持对元素流进行函数式操作的类 Stream,可以让你以一种声明的方式处理数据。

Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。

Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。

这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选排序聚合等。

元素流在管道中经过 中间操作(intermediate operation) 的处理,最后由 终端操作(terminal operation) 得到前面处理的结果。

大多stream操作接受某种形式的lambda表达式作为参数,通过方法接口的形式指定操作的具体行为,这些方法接口的行为基本上都是无干扰(non-interfering)和无状态(stateless)

  • 无干扰(non-interfering):该方法不修改stream的底层数据源。
  • 无状态(stateless):操作的执行是独立的,没有lambda表达式在执行中依赖可能发生变化的外部变量或状态。

示例源代码

stream的创建

  • 从一个 Collection 的 stream() 和 parallelStream() 方法;
  • 从一个数组通过 Arrays.stream(Object[]);
  • 来自流类上的静态工厂方法,例如 Stream.of(Object[]), IntStream.range(int, int) 或Stream.iterate(Object, UnaryOperator);
  • 文件的行可以从 BufferedReader.lines() 获得;
  • 文件路径流可以从 Files 中的方法获得;
  • 可以从 Random.ints() 获得随机数流;
  • JDK 中的许多其他流承载方法,包括 BitSet.stream()、Pattern.splitAsStream(java.lang.CharSequence) 和 JarFile.stream()。
private static void createStream() {
        List<String> list = new ArrayList<String>() {//这个大括号 就相当于我们  new 接口
            {//这个大括号 就是 构造代码块 会在构造函数前 调用
                this.add("hi");
                this.add("sorry");
                this.add("hello");
                this.add("word");
            }
        };
        // 从一个 Collection 的 stream() 和 parallelStream() 方法
        list.stream().map(it -> it + " dd").forEach(System.out::println);
        // 并发不保证执行顺序
        list.parallelStream().map(it -> it + " dd").forEach(System.out::println);

        String[] array = new String[]{
                "one", "two", "three"
        };
        // 从一个数组通过 Arrays.stream(Object[])
        Arrays.stream(array).map(it -> it + " dd").forEach(System.out::println);

        // 来自流类上的静态工厂方法
        IntStream.range(1, 5).forEach(System.out::println);
        // 无限流
        Stream.iterate(1, s -> {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return s + 1;
        }).forEach(System.out::println);

        // 文件的行可以从 BufferedReader.lines() 获得
        try {
            new BufferedReader(new FileReader("./test.txt")).lines().forEach(System.out::println);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        // 从 Random.ints() 获得随机数流
        new Random().ints(3, 1, 20).forEach(System.out::println);
    }

简单使用

中间操作

  • 过滤: filter(T -> boolean) (非干扰、无状态)
  • 类型转换: map(T -> R) 将流中的每一个元素 T 映射为 R (非干扰、无状态)
  • flatMap(T -> Stream) 将流中的每一个元素 T 映射为一个流,再把每一个流连接成为一个流
  • distinct() 将流中重复的元素去除,去重 (非干扰、有状态)
  • 根据字段排序 sorted() / sorted((T, T) -> int) (非干扰、有状态)
  • peek(T -> void)返回由该流的元素组成的流, 这个方法存在主要是为了支持调试,你想要的地方查看流经管道中某个点的元素
  • 返回前 n 个元素 limit(long n) (短路、有状态)
  • 跳过前 n 个元素 skip(long n) (有状态)
    private static void intermediateOperation() {
        String[] array = new String[]{
                "one, two", "four, five", "two, three", "three, four", "one, two", "six", "ten", "five", "three"
        };

        Arrays.stream(array)
                .filter(s -> s.startsWith("t"))
                .forEach(System.out::println);

        Arrays.stream(array)
                .map(s -> s.split(","))
                .flatMap(Arrays::stream)
                .peek(s -> System.out.println("flatMap -> " + s))
                .map(String::trim)
                .distinct()
                .sorted((String::compareTo))
                .limit(3)
                .skip(1)
                .forEach(System.out::println);
    }

终端操作

  • 遍历操作 forEach(e -> { … }) / forEachOrdered(e -> { … })
  • toArray() 返回一个包含此流元素的数组。
  • reduce((T, T) -> T)reduce(T, (T, T) -> T)reduce(R, (R, T) -> R, (R, R) -> R) 用于组合流中的元素,如求和,求积,求最大值等
  • collect() 收集方法,我们很常用的是 collect(toList()),当然还有 collect(toSet()) 等,参数是一个收集器接口
  • min()max()count() 最小值、最大值、流中元素数量
  • anyMatch(T -> boolean) 流中是否有一个元素匹配给定的 T -> boolean 条件
  • allMatch(T -> boolean) 流中是否所有元素都匹配给定的 T -> boolean 条件
  • noneMatch(T -> boolean) 流中是否没有元素匹配给定的 T -> boolean 条件
  • findAny():找到其中一个元素, 使用 stream() 时找到的是第一个元素;parallelStream() 并行时找到的是其中一个元素; findFirst():找到第一个元素; 值得注意的是,这两个方法返回的是一个 Optional 对象,它是一个容器类,能代表一个值存在或不存在
    private static void terminalOperation() {
        int[] array = new int[]{
                1, 2, 3, 4, 5, 2, 1, 3, 7
        };
        // reduce((T, T) -> T) 不接受任何起始值,但因为没有初始值,需要考虑结果可能不存在的情况,因此返回的是 Optional 类型
        OptionalInt res = Arrays.stream(array).reduce(Integer::sum);
        // reduce(T, (T, T) -> T) reduce 第一个参数 0 代表起始值为 0
        int res1 = Arrays.stream(array).reduce(0, Integer::sum);

        List<Widget> widgetList = Widget.genRandomWidgets(10);
        // reduce(R, (R, T) -> R, (R, R) -> R)
        int sumOfWeights = widgetList.stream().reduce(0, (sum, b) -> sum + b.getWeight(), Integer::sum);
        System.out.println("res = " + res + " res1 = " + res1 + " sumOfWeights = " + sumOfWeights);

        ArrayList<String> strings = Arrays.stream(array).collect(ArrayList::new, (c, e) -> c.add(e + ""), ArrayList::addAll);
        System.out.println(strings);

        List<String> weights = widgetList.stream().map(t -> t.getWeight() + "").collect(Collectors.toList());
        System.out.println(weights);

        OptionalInt min = Arrays.stream(array).min();
        OptionalInt max = Arrays.stream(array).max();
        long count = Arrays.stream(array).count();
        System.out.println("min = " + min + " max = " + max + " count = " + count);

        boolean anyMatch = Arrays.stream(array).anyMatch(t -> t > 5);
        boolean allMatch = Arrays.stream(array).allMatch(t -> t > 5);
        boolean noneMatch = Arrays.stream(array).noneMatch(t -> t > 5);
        System.out.println("anyMatch = " + anyMatch + " allMatch = " + allMatch + " noneMatch = " + noneMatch);

        OptionalInt findAny = Arrays.stream(array).parallel().findAny();
        OptionalInt findFirst = Arrays.stream(array).findFirst();
        System.out.println("findAny = " + findAny + " findFirst = " + findFirst);
    }

消耗品(Consumable)

流的元素在流的生命周期内仅被访问一次。与 Iterator 一样,必须生成新的流以重新访问源的相同元素。

        String[] array = new String[]{
                "one, two", "four"
        };

        Stream<String> stream = Stream.of(array);
        stream.findFirst();
        stream.findAny();

在执行上述代码时, 由于第一个终端操作 findFirst 已经消耗掉 stream, 导致执行第二个终端操作 findAny 时会抛出如下异常:

Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)
	at java.util.stream.ReferencePipeline.findAny(ReferencePipeline.java:469)
	at com.lkl.study.feature.stream.StreamTest.consumableTest(StreamTest.java:125)
	at com.lkl.study.feature.stream.StreamTest.main(StreamTest.java:16)

可以通过为每个最终操作(terminal operation)创建一个新的stream流的方式来解决上面的重用问题。

        String[] array = new String[]{
                "one, two", "four"
        };

        Stream<String> stream = Stream.of(array);
        stream.findFirst();
        stream = Stream.of(array);
        stream.findAny();

Supplier类可以在已经存在的中间操作(intermediate operations )的stream基础上构建一个新的stream。

        Supplier<Stream<String>> streamSupplier =
                () -> Stream.of(array)
                        .filter(s -> s.startsWith("t"));

        System.out.println(streamSupplier.get().anyMatch(s -> true));
        System.out.println(streamSupplier.get().noneMatch(s -> true));

streamSupplier的每次get()方法会构造一个新的stream,我们可以在这个stream上执行期望的最终操作(terminal operation)

参考文献

Stream

Java 8 Stream 教程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值