您当前的位置: 首页 > 

梁云亮

暂无认证

  • 2浏览

    0关注

    1211博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Stream API 基础

梁云亮 发布时间:2019-10-20 23:29:49 ,浏览量:2

1、Stream API简介
  • Stream关注的是对数据的运算,与CPU打交道;集合关注的是数据的存储,与内存打交道
  • Stream主要是用来对容器(集合、Map、数组、Optional)进行操作。Stream通过流水线的方式对容器类数据进行内部迭代处理。
  • 使用Stream API可以对内存中的数据进行过滤、排序、映射、 归约等操作、类似于SQL对数据库表的相关操作

流的特点:

  • Stream不会存储元素
  • Stream不会改变源对象。它会返回一个持有结果的新的Stream
  • Stream操作是延迟执行的,即它会等到真正需要结果的时候才执行
  • 只能遍历一次 :可以把流想象成一条流水线,流水线的源头是数据源,数据源中的元素依次被输送到流水线上,我们可以在流水线上对元素进行各种操作。一旦元素走到了流水线的另一头,那么这些元素就被“消费掉了”,我们无法再对这个流进行操作。当然,我们可以从数据源那里再获得一个新的流重新遍历一遍。
  • 采用内部迭代方式 :若要对集合进行处理,则需我们手写处理代码,这就叫做外部迭代。而要对流进行处理,我们只需告诉流我们需要什么结果,处理过程由流自行完成,这就称为内部迭代。   集合操作非常麻烦,若要对集合进行筛选、投影,需要写大量的代码,而流是以声明的形式操作集合,它就像SQL语句,我们只需告诉流需要对集合进行什么操作,它就会自动进行操作,并将执行结果交给你,无需我们自己手写代码。流的集合操作对我们来说是透明的,我们只需向流下达命令,它就会自动把我们想要的结果给我们。由于操作过程完全由Java处理,因此它可以根据当前硬件环境选择最优的方法处理,我们也无需编写复杂又容易出错的多线程代码了。
2、Stream常用的方法

流的操作分为中间操作和终端操作两种:

  • 中间操作:在执行的时候会返回一个流,多个中间操作可以串连起来形成一条流水线,在遇到终端操作的时候,一次性全部处理。 常见的中间操作有: map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 skip、 parallel、 sequential、 unordered……
  • 终端操作:会从流水线中返回一个执行结果。 常见的终端操作有:forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、iterator、anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit…… 在这里插入图片描述 在这里插入图片描述

Stream的使用步骤:

  1. 准备一个数据源:数据源可以是集合、数组、Options
  2. 创建Stream对象
  3. 执行中间操作:中间操作可以有多个,它们可以串连起来形成流水线。
  4. 执行终端操作:执行终端操作后本次流结束,将获得一个执行结果。
3、Stream的创建 示例1:创建流
public static void main(String[] args) {
        //通过集合创建
        List depts = DB.getDepts();
        Stream stream1 = depts.stream();//返回一个顺序流
        Stream stream2 = depts.parallelStream();//返回一个并行流

        //通过数组创建
        String[] array = {"zhangsan","lisi","wanger","maizi"};
        Stream stream3 = Arrays.stream(array);

        //通过Stream的of方法创建
        Stream stream4 = Stream.of("zhangsan", "lisi", "wanger", "mazi");

        //创建无限流
        Stream stream5 = Stream.iterate(0, item -> item + 2).limit(10);
    }
示例2:串行流转并行流
public static void main(String[] args) {
    List list =Arrays.asList(1,8,3,2,10,4,9,7,6);
    //获取并行流
    Stream parallelStream = list.parallelStream();
    //获取串行流
    Stream stream = list.stream();
    //串行流转并行流
    Stream parallelStream2 = stream.parallel(); 
}
示例3:Stream转集合或数组
public static void main(String[] args) {
    Stream stream = Stream.of("aa", "bb", "cc", "dd");
    //转换为List
    List list = stream.collect(Collectors.toList());
    //转换为Set
    Set set = stream.collect(Collectors.toSet());
    //转换为数组
    String[] array = stream.toArray(String[]::new);
}
示例3:Stream转字符串
public static void main(String[] args) {
    final String[] array = {"zhangsan", "lisi", "wanger", "mazi"};
    final String res1 = Stream.of(array).collect(Collectors.joining(""));
    System.out.println(res1);
    final Optional res2 = Stream.of(array).reduce(String::concat);
    System.out.println(res2.get());
}

结果: 在这里插入图片描述

4、中间操作 4.1、映射:map/flatMap
  • map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
  • flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。 flatMap通过一个 Function 把一个元素类型为 T 的流转换成元素类型为 R 的流,再把这些转换之后的流合并。 flatMap会把Stream中的层级结构扁平化,就是将最底层元素抽出来放到一起,最终输出的新 Stream 里面已经没有 List 了,都是直接的元素。 示例1:
     public static void main(String[] args) {
        List list = Arrays.asList("11,22,33", "aa,bb,cc");
        //将每个元素转成一个新的且不带逗号的元素
        list.stream()
                .map(s -> s.replaceAll(",", "."))
                .forEach(System.out::println);
    
        //将每个元素转换成一个stream
         list.stream()
                 .flatMap(item->Arrays.stream(item.split(",")))
                 .forEach(System.out::println);
    }
    
    结果: 在这里插入图片描述 示例2:
    public static void main(String[] args) {
        Stream.of("aa","bb","cc")
                .map(String::toUpperCase)
                .forEach(item-> System.out.print(item+"\t"));
        System.out.println();
        Stream.of(4,9,16)
                .map(Math::sqrt)
                .forEach(item-> System.out.print(item+"\t"));
        System.out.println();
        Stream.of(Arrays.asList("aa","bb"),Arrays.asList("11","22"))
                .map(item->item.stream())
                .forEach(System.out::println);
        Stream.of(Arrays.asList("aa", "bb"), Arrays.asList("11", "22"))
                .map(item -> item.stream())
                .forEach(item->item.forEach(System.out::print));
        System.out.println();
        Stream.of(Arrays.asList("aa","bb"),Arrays.asList("11","22"))
                .flatMap(item->item.stream())
                .forEach(System.out::println);
    }
    
    结果: 在这里插入图片描述

示例:一段非常有用的代码

public static void main(String[] args) {
    List res = List.of("ACCOUNTING", "RESEARCH")
            .stream()
            .map(temp -> new Dept(10, temp, temp))
            .collect(Collectors.toList());
    System.out.println(res);

    List list = List.of(new Dept(10, "ACCOUNTING", "NEWYORK"), new Dept(20, "RESEARCH", "DALLAS"))
            .stream()
            .map(temp -> temp.getDname())
            .collect(Collectors.toList());
    System.out.println(list);
}
4.2、排序:sorted
  • sorted():自然排序,流中元素需实现Comparable接口

  • sorted(Comparator com):定制排序,可以自定义Comparator排序器

    进行排序的类必须实现Comparable类,否则会报错cannot be cast to class java.lang.Comparable: 在这里插入图片描述 示例:

    public static void main(String[] args) {
        List depts = DB.getDepts();
        depts.stream().map(Dept::getDname).sorted().forEach(System.out::println);
        depts.stream().sorted((d1,d2)->{ //不要求Dept类实现omparable接口
            if(d1.getDname().equals(d2.getDname())) {
                return d1.getLoc().compareTo(d2.getLoc());
            }else {
                return d1.getDname().compareTo(d2.getDname());
            }
        }).forEach(System.out::println);
        depts.stream().sorted().forEach(System.out::println); //要求Dept类必须得实现Comparable接口  //----------- ①
    }
    

    结果: 在这里插入图片描述 编号①处必须得实现Comparable接口的Dept类的源代码:

    public class Dept implements Comparable{
        private Integer deptno;
        private String dname;
        private String loc;
        //……gettter/setter、默认构造方法、全参构造方法、toString()方法
        @Override
        public int compareTo(Dept o) {
            return this.getDname().compareTo(o.getDname());
        }
    }
    
4.3、筛选与切片:filter/limit/skip/distinct
  • filter:过滤流中的某些元素,只保留满足由 Predicate 所指定的条件的元素。
  • limit(n):截断流使其最多只包含指定数量的元素。
  • skip(n):返回一个新的流,并跳过原始流中的前 N 个元素。配合limit(n)可实现分页
  • distinct:通过方法hashCode() 和 equals() 来删除流中重复的元素 示例:调用顺序不一样,结果就不一样。
    public static void main(String[] args) {
            Stream.of("zhangsan","lisi","wanger","mazi","wanger","qianwu","wanger","zhaoliu","wanger")
                    .filter(item->item.length()!=4)
                    .skip(2)
                    .distinct()
                    .limit(3)
                    .forEach(System.out::println);
            Stream.of("zhangsan","lisi","wanger","mazi","wanger","qianwu","wanger","zhaoliu","wanger")
                    .filter(item->item.length()!=4)
                    .skip(2)
                    .limit(3)
                    .distinct()
                    .forEach(System.out::println);
        }
    
    结果: 在这里插入图片描述
4.4、peek

返回的流与原始流相同。当原始流中的元素被消费时,会首先调用 peek 方法中指定的 Consumer 实现对元素进行处理。 peek和map类似,但map接收的是一个Function表达式,有返回值;而peek接收的是Consumer表达式,没有返回值。

示例:

public static void main(String[] args) {
    Dept dept = new Dept(30, "SALES", "CHICAGO");
    Stream.of(dept)
            .map(item->item.getDname())
            .forEach(System.out::println);
    Stream.of(dept)
            .peek(item->item.setDname("AAA"))
            .forEach(System.out::println);
    Stream.of(dept)
            .peek(item->item.setDname("AAA"))
            .map(item->item.getDname())
            .forEach(System.out::println);
}

结果: 在这里插入图片描述

4.9、dropWhile

从原始流起始位置开始删除满足指定 Predicate 的元素,直到遇到第一个不满足 Predicate 的元素。

4.10、takeWhile

从原始流起始位置开始保留满足指定 Predicate 的元素,直到遇到第一个不满足 Predicate 的元素。

5、结束操作 5.1、匹配、聚合操作
  • allMatch:接收一个 Predicate 函数,当流中每个元素都符合该断言时才返回true,否则返回false
  • noneMatch:接收一个 Predicate 函数,当流中每个元素都不符合该断言时才返回true,否则返回false
  • anyMatch:接收一个 Predicate 函数,只要流中有一个元素满足该断言则返回true,否则返回false
  • findFirst:返回流中第一个元素
  • findAny:返回流中的任意元素
  • count:返回流中元素的总个数
  • max:返回流中元素最大值
  • min:返回流中元素最小值 示例:
public static void main(String[] args) {
    List list = Arrays.asList(18, 22, 3, 2, 15,7);

    boolean allMatch = list.stream().allMatch(e -> e > 13);
    System.out.println(allMatch);
    boolean noneMatch = list.stream().noneMatch(e -> e > 13);
    System.out.println(noneMatch);
    boolean anyMatch = list.stream().anyMatch(e -> e > 13);
    System.out.println(anyMatch);

    Optional firstOpt = list.stream().findFirst();
    System.out.println(firstOpt.get());
    Integer findAny = list.stream().findAny().get();
    System.out.println(findAny);

    long count = list.stream().count();
    System.out.println(count);
    Optional maxtOpt = list.stream().max(Integer::compareTo);
    System.out.println(maxtOpt.get());
    Integer min = list.stream().min(Integer::compareTo).get();
    System.out.println(min);
}

结果: 在这里插入图片描述

5.2、规约操作 Reduce
  • Optional reduce(BinaryOperator accumulator): 可以将流中元素反复结合起来,返回Optional< T >。 第一次执行时,accumulator函数的第一个参数为流中的第一个元素,第二个参数为流中元素的第二个元素;第二次执行时,第一个参数为第一次函数执行的结果,第二个参数为流中的第三个元素;依次类推。 示例:

    public static void main(String[] args)  {
        List list = Arrays.asList(1,3,2,6,8,3,9);
        Optional reduce = list.stream().reduce((x, y) -> x + y);
        System.out.println(reduce.get()); // 32
    }
    
  • T reduce(T identity, BinaryOperator accumulator) 可以将流中元素反复结合起来得到一个值,返回T。 第一次执行时,accumulator函数的第一个参数为identity,而第二个参数为流中的第一个元素。 示例1:

     public static void main(String[] args) {
        List list = Arrays.asList(1, 3, 2, 6, 8, 3, 9);
        Integer reduce = list.stream().reduce(10, (x, y) -> x + y);
        System.out.println(reduce); //42
    }
    

    示例2:

       public static void main(String[] args) {
            List list = Arrays.asList(1, 2, 3, 4);
            Optional reduce = list.stream()
                    .map(item -> item * 10)
                    .reduce((t1, t2) -> t1 + t2);
            System.out.println(reduce.get());  //100
            Integer res = list.stream().reduce(100, (i1, i2) -> i1 * i2);
            System.out.println(res);  //2400
        }
    
  • < U> U reduce(U identity,BiFunction accumulator,BinaryOperator< U> combiner)

    • 在串行流(stream)中,该方法跟第二个方法一样,即第三个参数combiner不会起作用。
    • 在并行流(parallelStream)中,流被fork join出多个线程进行执行,此时每个线程的执行流程就跟第二个方法reduce(identity,accumulator)一样,而第三个参数combiner函数,则是将每个线程的执行结果当成一个新的流,然后使用第一个方法reduce(accumulator)流程进行规约。 示例:
    public static void main(String[] args) {
        List list = Arrays.asList(1, 2, 3, 4);
        Integer res1 = list.stream().reduce(0,
                (x1, x2) -> {
                    System.out.println("accumulator: x1:" + x1 + "  x2:" + x2);
                    return x1 - x2;
                },
                (x1, x2) -> {
                    System.out.println("combiner: x1:" + x1 + "  x2:" + x2);
                    return x1 * x2;
                });
        System.out.println(res1); // -10
        Integer res2 = list.parallelStream().reduce(0,
                (x1, x2) -> {
                    System.out.println("accumulator: x1:" + x1 + "  x2:" + x2);
                    return x1 - x2;
                },
                (x1, x2) -> {
                    System.out.println("combiner: x1:" + x1 + "  x2:" + x2);
                    return x1 * x2;
                });
        System.out.println(res2); // -24
    }
    

    结果: 在这里插入图片描述

5.3、收集操作collect

将流转换为其他形式,接收一个Collector接口的实现,用于给Stream中元素做汇总的方法

collect方法的定义如下:

  • R collect(Collector
关注
打赏
1665409997
查看更多评论
立即登录/注册

微信扫码登录

0.0731s