zoukankan      html  css  js  c++  java
  • JAVA8 Stream流

    简介

    Stream 是一个 Collection 的增强工具,可以对集合进行各种操作,而且可以很方便的写出并发程序,学习之前需要了解一些函数,可以看 JAVA8 Lambda表达式。常见的获取方式就是 Collection.stream()

    操作类型

    操作类型分为两种

    • Intermediate(中间操作):可以多次使用,因为返回一个 Stream。比如 map(mapToInt, flatMap)filtersortedlimitskip
    • Terminal(结束操作):使用后就会结束。比如 forEachsortcollectmincountfindFirstanyMatch

    中间操作都是惰性的,也就是延迟的,所以会产生副作用,关于副作用会在之后的章节详细说明。

    创建

    Stream 的类型有三种 IntStreamDoubleStream LongStream,当然也可以使用 Stream<T>

    除了直接创建,还能通过 Collection 接口的 stream()parallelStream() 创建,其中 parallelStream() 创建的流是并发、线程不安全且操作无序的,虽然它是并发的,但仍有可能在某些操作下变回串行的,例如 forEachOrdered,此外还需要保证数据源是线程安全的。

    下面的代码展示了流的创建,第二行代码是取流中前三个元素在控制台输出,如果开启最后一行注释将会出现 java.lang.IllegalStateException 的异常,详细错误信息为 stream has already been operated upon or closed,所以创建的 Stream 仅能使用一次。

    IntStream intStream = IntStream.of(1, 2, 3, 4);
    intStream.limit(3).forEach(System.out::println);
    //intStream.limit(1).forEach(System.out::println);
    

    使用

    JAVA8 中 Stream 接口中的操作有 filtermapmapToIntmapToLongmapToDoubleflatMapflatMapToIntflatMapToLongflatMapToDoubledistinctsortedpeeklimitskipforEachforEachOrderedtoArrayreducecollectminmaxcountanyMatchallMatchnoneMatchfindFirstfindAny,方法很多,没见过的看注释、参数和返回值就懂了。

    示例使用 List 接口 的 stream 方法创建 Stream,下面是示例所需要的数据。

    //Get、Set、构造方法浪费空间,不粘贴了
    public class Person {
      //id
      private Integer id;
      //名字
      private String name;
      //年龄
      private Integer age;
      //组织
      private String organization;
    }
    
    //数据初始化
    List<Person> personList = new ArrayList<>();
    personList.add(new Person(1, "灰原哀", 7, "帝丹小学"));
    personList.add(new Person(2, "江户川柯南", 7, "帝丹小学"));
    personList.add(new Person(3, "宫野明美", 24, "黑衣组织"));
    personList.add(new Person(4, "赤井秀一", 27, "FBI"));
    personList.add(new Person(5, "贝尔摩德", 29, "黑衣组织"));
    

    下面是对 Stream 操作的两个简单示例。

    第一个将 personList 中 前 4 个、年龄为 7 的人提取出来,以组织和姓名为 keyvalue 组装成一个新的 Map,其中 Collectors.toMap 方法最后一个参数用于解决 key 冲突。

    第二个获取 personList 中名字为安室透的第一个人。

    Map<String, String> map = personList.stream().limit(4) //中间操作
            .filter(person -> Objects.equals(person.getAge(), 7)) //中间操作
            .collect(Collectors.toMap( // Collectors 类是随 Stream 一起引入的,即方便又好看,作用之一是收集元素到集合
                    Person::getOrganization, // map 的 key
                    Person::getName, // map 的 value
                    (old, now) -> old)); // 发生冲突的解决办法
    Optional<Person> optional = personList.stream()
            .filter(person->Objects.equals(person.getName(), "安室透")) //中间操作
            .findFirst();
    

    第二个方法返回值类型是 Optional<Person>Optional 是 JAVA8 中引入的一个容器,可以使用 get() 获取容器中的值,但 optional 中并没有值,所以会抛出 java.util.NoSuchElementException,为了解决这个问题可以使用 orElse(),当容器中值为空时返回设定的默认值,除了 orElse 还有 orElseGetorElseThrow。比如下面的这段代码返回了叫安室透的人。

    // optional 在上一段代码中产生的对象
    Person person = optional.orElse(new Person(7, "安室透", 29, "日本公安"));
    

    副作用

    对流的中间操作会产生副作用,结果是抛异常和数据的错误,它的来源有“干扰”和“有状态的 Lambda”。

    • “干扰”就是在中间操作时修改了流的数据源。比如在 forEach(Consumer<? super T> action) 中应该是消费数据,却给数据源添加了一个数据,结果是抛出了 java.util.ConcurrentModificationException 异常。
    • “有状态的 Lambda”,当后面操作产生的结果会被前面的操作影响时,前面操作的 Lambda 就被称作是有状态的。比如 有状态的 Lambda 的例子,例子中使用 parallelStream() 并发添加数据到 parallelStorage 中,结果就是 parallelStorage 中的数据顺序不可预测,因此称 e -> { parallelStorage.add(e); return e; } 是有状态的 Lambda

    总结

    本文没有深入介绍它的概念,只是简单介绍了 Stream 的使用和 Optional 容器,对于了解应该够了。

    参考资料

    为什么需要 Stream

    Side Effects

    Java 8 Stream 的终极技巧——Collectors 功能与操作方法详解

    Java8(3)Stream类的collect方法详解

  • 相关阅读:
    UDP协议
    发送大数据文件
    socket
    异常处理
    网络编程
    JupyterStudy——安装与环境部署
    PythonStudy——封装
    PythonStudy——继承、接口、鸭子类型
    PythonStudy——面向对象
    PythonStudy——xml 模块
  • 原文地址:https://www.cnblogs.com/hligy/p/13033718.html
Copyright © 2011-2022 走看看