java8之StreamAPI

Stream(流):

什么是stream(流):Stream是一个来自数据源的元素队列并支持聚合操作
    元素:是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算
    数据源:流的来源。可以是集合,数组,I/O channel, 产生器generator 等
    聚合操作:类似SQL语句一样的操作,如:filter, map, reduce, find, match, sorted等
​
Stream操作的两个基础特征:
    Pipelining: 中间操作都会返回流对象本身
        这样多个操作可以串联成一个管道,如同流式风格(fluent style)
        这样做可以对操作进行优化,比如延迟执行(laziness)和短路( short-circuiting)
    内部迭代: 
        以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代,这叫做外部迭代
        Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现
注意: 
    1. Stream自己不会存储元素
    2. Stream不会改变原对象,相反他们会返回一个持有结果的新Stream
    3. Stream操作是延迟执行,这意味着他们会等到需要结果的时候才执行

Stream操作步骤:

1.创建流(即初始化流)
2.中间操作:Intermediate
    一个流可以后面跟随零个或多个intermediate操作 
    做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用
    
    常见中间操作: 
        filter distinct sorted peek limit
        skip parallel sequential unordered
        map (mapToInt, flatMap 等)
3.终止操作:Terminal
    一个流只能有一个terminal操作,执行多个terminal操作会报错:stream has already been operated upon or closed
    Terminal 操作的执行,才会真正开始流的遍历并且会生成一个结果或者一个 side effect
    
    常见终止操作:
        forEach forEachOrdered toArray reduce collect
         min max count anyMatch allMatch noneMatch
         findFirst findAny iterator
    
    短路操作:Short-circuiting
        对于一个intermediate 操作:
            如果它接受的是一个无限大(infinite/unbounded)的Stream,但返回一个有限的新 Stream
        对于一个terminal 操作:
            如果它接受的是一个无限大的 Stream,但能在有限的时间计算出结果
        常见短路操作:
            anyMatch allMatch noneMatch findFirst findAny limit

流的创建:

方式一:Collection接口相关方法 将集合转换为流
    default Stream< E> stream();  //返回一个串行流 
    default Stream< E> parallelStream(); //返回一个并行流
    
    示例:
        List list = Arrays.asList("a","ab","abc","abcd","dh","");
        Stream listToStream = list.stream();//串行流
        Stream parallelStream = list.parallelStream();//并行流
        
    并行流通过默认的ForkJoinPool,可能提高你的多线程任务的速度。并行流在遍历时可能是无序的
    
方式二: Arrays类的静态方法 stream() 将数组转换为流
    public static  Stream stream(T[] array)
    
    示例:
        String [] strArray = new String[] {"a", "b", "c","d"};
        Stream arrayToStream = Arrays.stream(strArray);
        
方式三:Stream接口静态方法    of() 初始化流
    public static Stream of(T... values)
    
    示例:
        Stream stream = Stream.of("a","b","c","d");
​
方式四:Stream接口静态方法    iterate()、generat()创建无限流    
    public static Stream iterate(final T seed, final UnaryOperator f)
    public static Stream generate(Supplier s)
    
    示例:
        //0 2
        Stream iterateStream = Stream.iterate(0, (x) -> x+2).limit(2); 
        //0.7959655194696649 0.04986900509440961 0.4258300512930294 0.6815133817071739
        Stream generateStrem = Stream.generate(Math::random).limit(4);

流的常用操作:

  • 1.筛选/过滤:filter/distinct/limit/skip
方法 描述
filter(Predicate p) 接收 Lambda , 从流中排除某些元素
distinct() 筛选,通过流所生成元素的 hashCode() 和 equals() 去 除重复元素
limit(long maxSize) 截断流,使其元素不超过给定数量
skip(long n) 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素 不足 n 个,则返回一个空流。与 limit(n) 互补
    示例:
        Stream strStream = Stream.of("pugb","king","lol","ow","solo","ow","");
        
        strStream
            .filter(s -> s.length()>0)//过滤字符串长度小于0的元素
            .distinct() //元素去重 (需要重写 hashcode和 equals方法)
            .skip(2)    //跳过流的前两个元素
            .limit(2)   //从第三个元素起取2个元素
            .forEach(System.out::println);//内部迭代流中的元素
            
    执行结果:lol ow
  • 2.映射:map/flatMap/...
map(Function f) 接收一个函数作为参数,该函数会被应用到每个元 素上,并将其映射成一个新的元素
mapToDouble(ToDoubleFunction f) 接收一个函数作为参数,该函数会被应用到每个元 素上,产生一个新的 DoubleStream
mapToInt(ToIntFunction f) 接收一个函数作为参数,该函数会被应用到每个元 素上,产生一个新的 IntStream
mapToLong(ToLongFunction f) 接收一个函数作为参数,该函数会被应用到每个元 素上,产生一个新的 LongStream
flatMap(Function f) 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
    示例:
        List list = Arrays.asList("a","ab","abc","abcd","dh");
        list.stream()
            .map(String::toUpperCase)//将元素转为大写
            .forEach(System.out::println);//内部迭代流中的元素
       System.out.println("---------------------------------------")
       
       String[] arrayOfWords = {"Hello", "World"};
       List words = Arrays.asList(arrayOfWords);
       words.stream()
            .map(w -> split(""))
            .flatMap(Arrays::stream)
            .distinct()
            .forEach(System.out::println);
     执行结果:
         A AB ABC ABCD DH
         --------------------------------------------------
         H
         l
         o
         W
         r
         d
  • 3.排序:sorted 
方法 描述
sorted() 产生一个新流,其中按自然顺序排序
sorted(Comparator comp) 产生一个新流,其中按比较器顺序排序
    示例:
        List sortList = Arrays.asList(1,2,13,4,15,6,17,8,19);
        sortList.stream()
            .sorted()//排序
            .forEach(System.out::println);//内部迭代流中的元素
            
        sortList.stream()
            .sorted(Integer::compare)//排序
            .forEach(System.out::println);//内部迭代流中的元素
            
    执行结果:1 2 4 6 8 13 15 17 19
  • 4.查找与匹配: 
方法 描述
allMatch(Predicate p) 检查是否匹配所有元素
anyMatch(Predicate p) 检查是否至少匹配一个元素
noneMatch(Predicate p) 检查是否没有匹配所有元素
findFirst() 返回第一个元素
findAny() 返回当前流中的任意元素
count() 返回流中元素总数
max(Comparator c) 返回流中最大值
min(Comparator c) 返回流中最小值
forEach(Consumer c) 内部迭代(使用 Collection 接口需要用户去做迭 代,称为外部迭代。相反,Stream API 使用内部 迭代——它帮你把迭代做了)
    示例:
        List matchList = Arrays.asList("pugb","king","lol","ow","solo","ow","");
        boolean anyMatch = matchList.stream().anyMatch(str -> str.equals("king"));
        boolean noneMatch = matchList.stream().noneMatch(String::isEmpty);
        boolean allMatch = matchList.stream().allMatch(String::isEmpty);
        System.out.println(anyMatch);   //true
        System.out.println(noneMatch);  //false
        System.out.println(allMatch);   //false
​
        Optional findFirst = matchList.stream().findFirst();
        System.out.println(findFirst.get());    //pugb
        
        Optional findAny = matchList.stream().filter(s -> s.equals("king")).findAny();
        System.out.println(findAny.get());  //king
  •  5.统计:max/min/avg/sum

    示例:
        List integers = Arrays.asList(1,2,13,4,15,6,17,8,19);
        IntSummaryStatistics stats = integers.stream().mapToInt((x) ->x).summaryStatistics();
        System.out.println("列表中最大的数 : " + stats.getMax());      //19                   
        System.out.println("列表中最小的数 : " + stats.getMin());      //1                    
        System.out.println("所有数之和 : " + stats.getSum());        //85                   
        System.out.println("平均数 : " + stats.getAverage());      // 9.444444444444445   
        System.out.println("=========================================================");
        
        OptionalInt max = integers.stream().mapToInt((x) ->x).max();
        OptionalInt min = integers.stream().mapToInt((x) ->x).min();
        int sum = integers.stream().mapToInt((x) ->x).sum();
        OptionalDouble avg = integers.stream().mapToInt((x) ->x).average();
        System.out.println("最大值: " +max.getAsInt());        //19
        System.out.println("最小值: " +min.getAsInt());        //1
        System.out.println(" 和:"  +sum);                        //85
        System.out.println("平均值: "  +avg.getAsDouble());    // 9.444444444444445
        System.out.println("=========================================================");
        
        //Stream中只有max和min两个方法 sum/avg需转换为对应的包装类Stream计算
        Optional maxInteger = integers.stream().max(Integer::compare);
        Optional minInteger = integers.stream().min(Integer::compare);
        System.out.println(maxInteger.get());       //19
        System.out.println(minInteger.get());       //1
  •  6.归约:reduce
方法 描述
reduce(T iden, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。 返回 T
reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。 返回 Optional< T>
    备注:map 和 reduce 的连接通常称为 map-reduce 模式,因 Google 用它 来进行网络搜索而出名
    
    示例:
        //求和
        List list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        Integer sum = list.stream().reduce(0, (x, y) -> x + y);
        System.out.println(sum);        //55 
  • 7.收集:collect 
    准备Employee类:重写hashCode和equals方法
        public class Employee {
            private Integer id;
            private String name;
            private Integer age;
            private Double salary;
            private Integer status;
            
            //省略getter/setter/toString/hashCode/equals方法
        }
    定义集合:
        List emps = Arrays.asList(
                  new Employee(101, "林青霞", 28, 9889.99),
                  new Employee(102, "东方不败", 29, 4329.85),
                  new Employee(103, "周星驰", 40, 1233.88),
                  new Employee(104, "大圣", 500, 5000.44),
                  new Employee(105, "张无忌", 15, 3000.09),
                  new Employee(102, "东方不败", 29, 4329.85)
          );
    收集示例:
        emps.stream()
            .map(Employee::getName)//将员工姓名转为stream
            .collect(Collectors.toList())//将员工姓名stream转为List集合
            .forEach(System.out::println);
        emps.stream()
            .map(Employee::getName)//将员工姓名转为stream
            .collect(Collectors.toSet())//将员工姓名stream转为Set集合
            .forEach(System.out::println);
        emps.stream()
        .map(Employee::getName)//将员工姓名转为stream
        .collect(Collectors.toCollection(HashSet::new))//将员工姓名stream转为HashSet集合
        .forEach(System.out::println);

        执行结果:
            林青霞 东方不败 周星驰 大圣 张无忌 东方不败
            ------------------- 
            周星驰 林青霞 大圣 东方不败 张无忌 
            ------------------- 
            周星驰 林青霞 大圣 东方不败 张无忌
    统计示例:
        Long count = emps.stream().collect(Collectors.counting());
        System.out.println("员工个数:" + count);
        Double avgSalary = emps.stream().
                collect(Collectors.averagingDouble(Employee::getSalary));
        System.out.println("平均工资:" + avgSalary);
        Double sumSalary= emps.stream().
                collect(Collectors.summingDouble(Employee::getSalary));
        System.out.println("总工资:" + sumSalary);
        Optional maxSalary = emps.stream()
                .collect(Collectors.maxBy((x, y) -> Double.compare(x.getSalary(), y.getSalary())));
        System.out.println("最高工资员工:" + maxSalary.get());
        Optional minSalary = emps.stream()
                 .map(Employee::getSalary)
                 .collect(Collectors.minBy(Double::compare));
        System.out.println("最低工资:" + minSalary.get());
        //统计分析
        DoubleSummaryStatistics doubleSummaryStatistics = emps.stream()
                 .collect(Collectors.summarizingDouble(Employee::getSalary));
        System.out.println("平均工资:" + doubleSummaryStatistics.getAverage());
        System.out.println("最高工资:" + doubleSummaryStatistics.getMax());
        System.out.println("最低工资:" + doubleSummaryStatistics.getMin());
        System.out.println("总工资:" + doubleSummaryStatistics.getSum());
        //拼接
        String join = emps.stream()
                 .map(Employee::getName)//将员工姓名转为stream
                 .collect(Collectors.joining(",", "--", "--"));//拼接符"," 前缀"--" 后缀"--"
        System.out.println(join);
        
        
        执行结果:
            员工个数:6
            平均工资:4630.683333333333
            总工资:27784.1
            最高工资员工:Employee [id=101, name=林青霞, age=28, salary=9889.99, status=null]
            最低工资:1233.88
            平均工资:4630.683333333333
            最高工资:9889.99
            最低工资:1233.88
            总工资:27784.1
            --林青霞,东方不败,周星驰,大圣,张无忌,东方不败--
             
    收集分组:
        Map> group = emps.stream()
                .collect(Collectors.groupingBy(Employee::getName));
        System.out.println(group);
        
        执行结果:
            {
             周星驰=[Employee [id=103, name=周星驰, age=40, salary=1233.88, status=null]],
             林青霞=[Employee [id=101, name=林青霞, age=28, salary=9889.99, status=null]], 
             大圣=[Employee [id=104, name=大圣, age=500, salary=5000.44, status=null]],
             东方不败=[Employee [id=102, name=东方不败, age=29, salary=4329.85, status=null],                          
                      Employee [id=102, name=东方不败, age=29, salary=4329.85, status=null]
             ],
             张无忌=[Employee [id=105, name=张无忌, age=15, salary=3000.09, status=null]]}
    多级分组:
        Map>> moreGroup = emps.stream()
                .collect(Collectors.groupingBy(Employee::getName, Collectors.groupingBy((e) -> {
                    if (e.getAge() < 30) return "青年";
                    else if (e.getAge() < 50) return "中年";
                    else return "老年";
                })));
        System.out.println(moreGroup);
        
        执行结果:
            {
            周星驰={中年=[Employee [id=103, name=周星驰, age=40, salary=1233.88, status=null]]},             
            林青霞={青年=[Employee [id=101, name=林青霞, age=28, salary=9889.99, status=null]]},             
            大圣={老年=[Employee [id=104, name=大圣, age=500, salary=5000.44, status=null]]}, 
            东方不败={青年=[Employee [id=102, name=东方不败, age=29, salary=4329.85, status=null],                           
                          Employee [id=102, name=东方不败, age=29, salary=4329.85, status=null]]
            }, 
            张无忌={青年=[Employee [id=105, name=张无忌, age=15, salary=3000.09, status=null]]}}

 Collector 接口中方法的实现决定了如何对流执行收集操作(如收 集到 List、Set、Map)。但是 Collectors 实用类 供了很多静态 方法,可以方便地创建常见收集器实例,具体方法与实例如下表:

方法 返回类型 作用
toList List 把流中元素收集到List
List emps= list.stream().collect(Collectors.toList());
toSet Set 把流中元素收集到Set
Set emps= list.stream().collect(Collectors.toSet());
toCollection Collection 把流中元素收集到创建的集合
Collectionemps=list.stream().collect(Collectors.toCollection(ArrayList::new));
counting Long 计算流中元素的个数
long count = list.stream().collect(Collectors.counting());
summingInt Integer 对流中元素的整数属性求和
inttotal=list.stream().collect(Collectors.summingInt(Employee::getSalary));
averagingInt Double 计算流中元素Integer属性的平均 值
doubleavg= list.stream().collect(Collectors.averagingInt(Employee::getSalary));
summarizingInt IntSummaryStatistics 收集流中Integer属性的统计值。 如:平均值
IntSummaryStatisticsiss= list.stream().collect(Collectors.summarizingInt(Employee::getSalary));
joining String 连接流中每个字符串
String str= list.stream().map(Employee::getName).collect(Collectors.joining());
maxBy Optional 根据比较器选择最大值
Optionalmax= list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary)));
minBy Optional 根据比较器选择最小值
Optional min = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary)));
reducing 归约产生的类型 从一个作为累加器的初始值 开始,利用BinaryOperator与 流中元素逐个结合,从而归 约成单个值
inttotal=list.stream().collect(Collectors.reducing(0, Employee::getSalar, Integer::sum));
collectingAndThen 转换函数返回的类型 包裹另一个收集器,对其结 果转换函数
inthow= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));
groupingBy Map> 根据某属性值对流分组,属 性为K,结果为V
Map> map= list.stream() .collect(Collectors.groupingBy(Employee::getStatus));
partitioningBy Map> 根据true或false进行分区
Map>vd= list.stream().collect(Collectors.partitioningBy(Employee::getManage));

 

你可能感兴趣的:(java8)