您的位置:首页 > 编程语言 > Java开发

JAVA基础7-Stream基础学习笔记

2019-07-07 21:05 344 查看

Stream基础学习笔记

  • Stream创建方法
  • Stream通用语法
  • Stream的常用方法
  • 最终操作(归纳(reduce))
  • Comparator
  • Collectors
  • 并行操作
  • 其他说明
  • Stream概念

    来源

    Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。
    Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。

    概念

    Stream(流)是一个来自数据源的元素队列并支持聚合操作
    1) 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
    2) 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
    3) 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。

    特点

    和以前的Collection操作不同, Stream操作还有两个基础的特征:
    1) Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
    2) 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。

    特性

    1) stream不存储数据
    2) stream不改变源数据
    3) stream的延迟执行特性

    Stream创建方法

    通过Stream静态方法

    /**
    * 通过一个Supplier函数,无限生成对象的集合流,也是一个无限流
    */
    public static<T> Stream<T> generate(Supplier<T> s);
    例子:
    Stream.generate(Math::random).limit(10).forEach(System.out::println);
    /**
    * 生成无限长度的Stream,和generator不同的是,其元素的生成是重复对给定的种子值(seed)调用用户指定函数来生成的。其中包含的元素可以认为是:seed,f(seed),f(f(seed))无限循环
    */
    public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f);
    例子:
    Stream.iterate(1, e->e+2).limit(10).forEach(System.out::println);
    /**
    * 通过一个数组获取一个stream,元素为数组的元素,元素必须是对象,而不能是原始数据类型(int、double等)
    */
    public static<T> Stream<T> of(T t)
    例子:
    String[] arrs = new String[]{"a","b","c","d"};
    Stream.of(arrs).forEach(System.out::println);
    /**
    * 通过多个对象入参获取一个stream,元素是入参对象
    */
    public static<T> Stream<T> of(T... values);
    例子:
    Stream.of(new String("one"),new String("two")).forEach(System.out::println);

    通过collection方法

    可以用过集合的接口的默认方法,创建一个流;使用这个方法,包括继承Collection的接口,如:Set,List等等。

    /**
    * 创建一个流
    */
    default Stream<E> stream();
    
    /**
    * 创建一个并行流
    */
    default Stream<E> parallelStream()

    例子:

    List<String> strList = new ArrayList<String>();
    strList.add("lin");
    strList.add("wu");
    strList.stream().forEach(System.out::println);
    strList.parallelStream().forEach(System.out::println);

    通过Arrays静态方法

    /**
    * 通过一个数据获取流,这泛型的,还存在int、long和double类型
    */
    public static <T> Stream<T> stream(T[] array);
    例子:
    Arrays.stream(new int[]{1,2,3,4}).forEach(System.out::println);

    Stream通用语法


    Stream的操作分为3部分:
    第一部分是创建一个stream流(可以参照Stream创建方法)
    第二部分是stream流的中间操作,返回的还是一个stream流(此部分可以有也可以没有)
    第三部分是stream流的最终操作

    Stream的常用方法

    中间操作(转换)

    无状态操作

    map(转化元素)

    /**
    * 通过实现Function函数接口,遍历转化元素
    */
    <R> Stream<R> map(Function<? super T, ? extends R> mapper);

    filter(过滤元素)

    /**
    * 通过实现Predicate函数接口,遍历元素,做过滤使用
    */
    Stream<T> filter(Predicate<? super T> predicate);

    flatMap(拆解元素)

    /**
    * 将每个元素作为流传进Function中,最后归入父类流里面
    */
    <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

    peek(监视元素)

    /**
    * 此方法主要用于支持调试,您希望在元素流经管道中的某个点时查看元素
    */
    Stream<T> peek(Consumer<? super T> action);

    有状态操作

    limit(截取元素)

    /**
    * 将取流的前几个元素
    */
    Stream<T> limit(long maxSize);

    sorted(排序元素)

    /**
    * 将元素通过自然排序,其元素必须实现Comparable接口
    */
    Stream<T> sorted();
    
    /**
    * 将元素通过自己实现的Comparable接口进行排序
    */
    Stream<T> sorted(Comparator<? super T> comparator);

    distinct(去重元素)

    /**
    * 将元素做去重操作,使用equals作为比较
    */
    Stream<T> distinct();

    skip(跳过元素)

    /**
    * 跳过前n个元素
    */
    Stream<T> skip(long n);

    最终操作(归纳(reduce))

    非短路操作

    max(最大值)

    /**
    * 通过一个Comparator接口,返回最大值元素
    */
    Optional<T> max(Comparator<? super T> comparator);

    min(最小值)

    /**
    * 通过一个Comparator接口,返回最小值元素
    */
    Optional<T> min(Comparator<? super T> comparator);

    count(数量)

    /**
    * 获取元素个数
    */
    long count();

    reduce(归纳)
    理解为归档函数,简单理解为最后获取一个归纳的值(可能是总和、最大值、最小值等),前面所有函数都可以通过reduce实现,而之所以有上面的函数,是因为常用才定义出来。

    /**
    * identity为初始值,然后通过BinaryOperator操作,得到最后的值(与元素相同类型)
    */
    T reduce(T identity, BinaryOperator<T> accumulator);
    例子:实现sum操作
    int[] arrInt = {1,3,4,2};
    System.out.println("ints sum is:" + Arrays.stream(arrInt).reduce(0, (sum, item) -> sum + item));
    
    /**
    * 没有初始值,然后通过BinaryOperator操作,得到最后的值(是一个Optional值)
    */
    Optional<T> reduce(BinaryOperator<T> accumulator);
    例子:实现max操作
    int[] arrInt = {1,3,4,2};
    System.out.println("ints max is:" + Arrays.stream(arrInt).reduce(0, (item1, item2) -> item1 >item2?item1:item2));
    
    /**
    * identity为初始值
    * 当stream是非并行操作时,用法与2个参数的reduce基本一样,通过BiFunction操作,得到最后的值(与元素的类型不一定相同)
    * 当stream是并行操作时,先是每个线程使用初始值identity分别进行BiFunction操作,再将得到的每个结果进行BinaryOperator操作
    */
    <U> U reduce(U identity,
    BiFunction<U, ? super T, U> accumulator,
    BinaryOperator<U> combiner);

    例子1:通过非并行实现过滤filter操作

    List<Integer> ints = Arrays.asList(1,2,3,4,5,6,7,8,9,10);		 BiFunction<ArrayList<Integer>,Integer,ArrayList<Integer>> bifun = new BiFunction<ArrayList<Integer>,Integer,ArrayList<Integer>>(){
    @Override
    public ArrayList<Integer> apply(ArrayList<Integer> t, Integer u) {
    if(u<5)
    t.add(u);
    return t;
    }
    
    };
    BinaryOperator<ArrayList<Integer>> binaryOper = new BinaryOperator<ArrayList<Integer>>() {
    @Override
    public ArrayList<Integer> apply(ArrayList<Integer> strings, ArrayList<Integer> strings2) {
    return strings;
    }
    };
    System.out.println("ints filter less than 5 is:" + ints.stream().reduce(new ArrayList<Integer>(),bifun,binaryOper));

    collect(收集)
    可以理解与reduce一样,但是实现方式可能不同,reduce中通过BiFunction有返回值,而collect通过BiConsumer没有返回值,可能造成影响是对于原始数据类型和String,在collect中每一次BiConsumer操作都无法保留。reduce适合不可变容器归约,collect适合可变容器归约。collect适合并行。

    /**
    * supplier为初始值,但与reduce不同是它接受一个表达式
    * accumulator,对每个元素进行操作
    * combiner,将所有线程结果进行combiner操作
    */
    <R> R collect(Supplier<R> supplier,
    BiConsumer<R, ? super T> accumulator,
    BiConsumer<R, R> combiner);
    /**
    * 通过Collector工具类实现与上面方法一致效果
    */
    <R, A> R collect(Collector<? super T, A, R> collector);

    例子:实现filter功能

    List<Integer> ints = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
    System.out.println("ints filter less than 5 is:" + ints.stream().parallel().collect(()->new ArrayList<Integer>(), (array, ar) -> {if (ar<5) array.add(ar);}, (array1, array2) -> array1.addAll(array2)));

    短路操作

    findFirst(第一个元素)

    /**
    * 获取第一个元素
    */
    Optional<T> findFirst();

    findAny(任意一个元素)

    /**
    * 返回这个集合中,取到的任何一个对象。在串行流中取第一个,在并行流中随机取。
    */
    Optional<T> findAny();

    anyMatch(任何一个元素符合条件)

    /**
    * 元素中只要一个符合predicate条件,即返回true
    */
    boolean anyMatch(Predicate<? super T> predicate);

    allMatch(所有元素符合条件)

    /**
    * 所有元素中都符合predicate条件,即返回true
    */
    boolean allMatch(Predicate<? super T> predicate);

    noneMatch(没有元素符合条件)

    /**
    * 所有元素中都不符合predicate条件,即返回true
    */
    boolean noneMatch(Predicate<? super T> predicate);

    Comparator

    Comparator接口,提供了一下静态方法获取comparator。
    1) Comparator.reverseOrder()) 自然排序的逆向排序
    2) Comparator.comparing(Student::getAge))使用某个属性排序
    3) Comparator.comparing(Student::getAge).reversed(),逆向排序
    一般用于sort操作

    Collectors

    Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors 可用于返回列表或字符串。一般用于collect操作中。
    toList(转换为List)

    /**
    * 将stream转化为一个List
    */
    public static <T> Collector<T, ?, List<T>> toList();

    toSet(转换为Set)

    /**
    * 将stream转化为一个Set
    */
    public static <T> Collector<T, ?, Set<T>> toSet();

    toMap(转换为Map)

    /**
    * 将stream转化为一个Map
    * keyMapper,是获取key的一个function,如果元素的key存在一样,会报java.lang.IllegalStateException。这是由于没有传入mergeFunction,使用默认的throwingMerger。
    * valueMapper,是获取value的一个function
    */
    public static <T, K, U>
    Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
    Function<? super T, ? extends U> valueMapper);
    
    /**
    * 将stream转化为一个Map
    * keyMapper,是获取key的一个function,如果元素的key存在一样,会报java.lang.IllegalStateException
    * valueMapper,是获取value的一个function
    * mergeFunction,一个BinaryOperator,解决一个重复key报错问题
    */
    public static <T, K, U>
    Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
    Function<? super T, ? extends U> valueMapper,
    BinaryOperator<U> mergeFunction);
    
    /**
    * 将stream转化为一个Map
    * keyMapper,是获取key的一个function,如果元素的key存在一样,会报java.lang.IllegalStateException
    * valueMapper,是获取value的一个function
    * mergeFunction,一个BinaryOperator,解决一个重复key报错问题
    * mapSupplier,指定返回Map类型,如果没有,默认为HashMap。
    */
    public static <T, K, U, M extends Map<K, U>>
    Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
    Function<? super T, ? extends U> valueMapper,
    BinaryOperator<U> mergeFunction,
    Supplier<M> mapSupplier)

    例子:

    List<Person> list = new ArrayList<Person>();
    list.add(new Person(1, "haha"));
    list.add(new Person(2, "rere"));
    list.add(new Person(3, "fefe"));
    list.add(new Person(1, "linwu"));
    list.add(new Person(4, null));
    Map<Integer, Person> mapp = list.stream().collect(Collectors.toMap(Person::getId, Function.identity(),(k,v)->v));
    System.out.println(mapp);
    System.out.println(mapp.get(1).getName());

    averaging(获取平均值)
    有三种,分别为averagingInt、averagingLong、averagingDouble。返回平均值。
    summarizing(获取统计信息)
    有三种,分别为summarizingInt、summarizingLong、summarizingDouble。返回一个统计信息,包括个数,总数等。
    groupingBy(分组)
    分组功能,返回一个Map,key为泛型,value为一个List。有三种类型,可以支持多层分组。
    partitioningBy(分区)
    分区功能,是分组功能的一种特殊情况,其map的key是boolean,所以只有true和false。可以支持多层分组。

    并行操作

    根据预估的数据量获取最小处理单元的大小阈值,即当数据量已经小于这个阈值的时候进行计算,否则进行fork 将任务划分成更小的数据块,进行求解。这里值得注意的是,getTargetSize 在第一次调用的时候会设置:
    预测数据量大小 / (默认并发度 * 4) 的结果作为最小执行单元的数量(配置的默认值是cpu 数 – 1,可以通过java.util.concurrent.ForkJoinPool.common.parallelism设置)

    其他说明

    Optional操作

    通常聚合操作会返回一个Optional类型,Optional表示一个安全的指定结果类型,所谓的安全指的是避免直接调用返回类型的null值而造成空指针异常,调用optional.ifPresent()可以判断返回值是否为空,或者直接调用ifPresent(Consumer<? super T> consumer)在结果部位空时进行消费操作;调用optional.get()获取返回值。

    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: