您当前的位置: 首页 >  Java

cuiyaonan2000

暂无认证

  • 0浏览

    0关注

    248博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Java 8 Stream

cuiyaonan2000 发布时间:2021-03-25 18:27:46 ,浏览量:0

序言

       强化下Stream,现在越来越多的框架和工具在使用类似的特性作为自己的语言了。其实主要是熟悉java8所提供的中间方法和最终方法。这些方法在python spark,flink,中都会看到,所以是个很重要的一个变成风格,且相互之间肯定有关联的,对于学习其它框架会有很大的帮助cuiyaonan2000@163.com

简介

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

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

什么是 Stream?

Stream(流)是一个来自数据源的元素队列并支持聚合操作

  • 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
  • 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
  • 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。-------------这个是熟悉的重点cuiyaonan2000@163.COM

和以前的Collection操作不同, Stream操作还有两个基础的特征:

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

生成流的方式

在 Java 8 中, 集合接口有两个方法来生成流:

  • stream() − 为集合创建串行流。

  • parallelStream() − 为集合创建并行流

reduce

reduce的作用就是遍历集合,计算累加每个元素的值,然后返回一个对象. 这里的元素不是一定要数字,也可以是对象.因为计算方法是我们自己写的.

因为是累加所以就会有中间结果值和初始值的设置.其它的没人什么cuiyaonan2000@163.com

package nan.yao.cui.others;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collector;
import java.util.stream.Collectors;

/**
 * @Author: cuiyaonan2000@163.com
 * @Description: todo
 * @Date: Created at 2023-1-6  8:56
 */
public class Test4 {

    public static void main(String[] args){


        List sleepList = new ArrayList();

        sleepList.add(1);
        sleepList.add(2);
        sleepList.add(3);

        //如下 a 跟 b这2个参数 .a 是中间结果,即上一个a+b的结果,b是List中的轮询数据. a第一次取值为List的第一个元素
        System.out.println(sleepList.stream().reduce((a,b)->a+b).toString());

        //这里的10 是第一次运行的a的值初始值.而不是从List中获取值
        System.out.println(sleepList.stream().reduce(10,(a,b)->a+b).toString());

        //同上temList即最终的结果的类型,加初始化A的值
        //a 就是temList,b是List的循环元素
        //a1 b2就是parallel()多线程的结果计算返回.那这里的result感觉就是个废物.
        List temList = new ArrayList();
        Integer result =0;
         Integer aa = sleepList.stream().parallel().reduce(result,(a,b)->{
            return a+b;
        },(a1,b1)->{
             return a1+b1;
         });
         System.out.println(temList.size());
        System.out.println(aa);
    }



}

peek

peek主要用于对遍历的数据进行一些操作,但是对数据影响并不会改变原来的的值,因为修改后的值不会返还给流中. 所以主要是用来遍历流中的对象.(也有特例如果我们遍历的是  对象 即引用,则会改变.cuiyaonan2000@163.com)

另外特别注意peek是中间操作,如果在它后面没有跟最终操作,则该peek中的方法不会运行cuiyaonan2000@163.com

通过peek的入参是Consumer,Map的入参是Function

  • Consumer是没有返回值的,它只是对Stream中的元素进行某些操作,但是操作之后的数据并不返回到Stream中,所以Stream中的元素还是原来的元素。
  • 而Function是有返回值的,这意味着对于Stream的元素的所有操作都会作为新的结果返回到Stream中。
Stream.of("one", "two", "three","four").peek(u -> u.toUpperCase())
                .forEach(System.out::println);
展示结果:

one
two
three
four



Stream.of("one", "two", "three","four").map(u -> u.toUpperCase())
                .forEach(System.out::println);

展示结果:

ONE
TWO
THREE
FOUR

forEach

用于遍历集合,不会返回内容。我们可对数据进行显示和计算的操作。

Stream 提供了新的方法 'forEach' 来迭代流中的每个数据。以下代码片段使用 forEach 输出了10个随机数:

Random random = new Random();
random.ints().limit(10).forEach(System.out::println);

map

该方法表示将 队列中的或者流中的对象进行处理,返回一个新的对象(当然也可以返回原来的对象,对比foreach就是每个对象处理完了,有另外一个对象返回cuiyaonan2000@163.com)

map 方法用于映射每个元素到对应的结果,以下代码片段使用 map 输出了元素对应的平方数:

List numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
// 获取对应的平方数
List squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
flatMap

flatmap与map的区别就是:flatmap会在过滤集合中的集合对象中时,按照一个集合放回。map则会把集合中的集合原样返回。

       List eggs = new ArrayList();


        // 第一箱鸡蛋
        eggs.add(new String[]{"鸡蛋_1", "鸡蛋_1", "鸡蛋_1", "鸡蛋_1", "鸡蛋_1"});
        // 第二箱鸡蛋
        eggs.add(new String[]{"鸡蛋_2", "鸡蛋_2", "鸡蛋_2", "鸡蛋_2", "鸡蛋_2"});
 

        eggs.stream()
                .map(x -> Arrays.stream(x).map(y -> y.replace("鸡", "煎")))
                .forEach(x -> System.out.println("组" + group++ + ":" + Arrays.toString(x.toArray())));

        /*
        控制台打印:------------
        组1:[煎蛋_1, 煎蛋_1, 煎蛋_1, 煎蛋_1, 煎蛋_1]
        组2:[煎蛋_2, 煎蛋_2, 煎蛋_2, 煎蛋_2, 煎蛋_2]
         */



        eggs.stream()
                .flatMap(x -> Arrays.stream(x).map(y -> y.replace("鸡", "煎")))
                .forEach(x -> System.out.println("学生" + student++ + ":" + x));
        /*
        控制台打印:------------
        学生1:煎蛋_1
        学生2:煎蛋_1
        学生3:煎蛋_1
        学生4:煎蛋_1
        学生5:煎蛋_1
        学生6:煎蛋_2
        学生7:煎蛋_2
        学生8:煎蛋_2
        学生9:煎蛋_2
        学生10:煎蛋_2
         */

filter

该方法是过滤,与map的区别是:map每个对象都会返回,filter只返回满足条件的

filter 方法用于通过设置的条件过滤出元素。以下代码片段使用 filter 方法过滤出空字符串:

Liststrings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
// 获取空字符串的数量
long count = strings.stream().filter(string -> string.isEmpty()).count();
limit

用于控制队列或者流中的,数据项数量

limit 方法用于获取指定数量的流。 以下代码片段使用 limit 方法打印出 10 条数据:

Random random = new Random();
random.ints().limit(10).forEach(System.out::println);

sorted

这个就是将队列或者流中的数据项进行排序。

根据什么排序呢,如果是对象可以使用Comparator.comparing(Person::getAge),其中Person是类,getAge是方法。用getAge的返回内容进行排序升序,使用.reversed()来进行逆序cuiyaonan2000@163.com

//sorted 方法用于对流进行排序。以下代码片段使用 sorted 方法对输出的 10 个随机数进行排序:
Random random = new Random();
random.ints().limit(10).sorted().forEach(System.out::println);


//自然排序 升序
List listTem = list.stream().sorted() 
//逆序
List listTem = list.stream().sorted(Comparator.reverseOrder()) 


//升序
List listTem = list.stream().sorted(Comparator.comparing(Person::getAge)) 

//逆序
List listTem = list.stream().sorted(Comparator.comparing(Person::getAge).reversed()) 

终端操作 Collectors---------结果返回

这个就是将我们处理过后的数组或者流的结果返回。注意该写法。另外以上实例中的count()等方法,其实就是集合或者流的统计方法,针对的是整体结果集cuiyaonan2000@163.com

Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors 可用于返回列表或字符串:

Liststrings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
 
System.out.println("筛选列表: " + filtered);
String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("合并字符串: " + mergedString);
Collectors的其它方法(也可以自定义Collector,看需求)

首先归类操作已经停了很多有用的.

统计

另外,一些产生统计结果的收集器也非常有用。它们主要用于int、double、long等基本类型上,它们可以用来产生类似如下的统计结果。

List numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
 
IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics();
 
System.out.println("列表中最大的数 : " + stats.getMax());
System.out.println("列表中最小的数 : " + stats.getMin());
System.out.println("所有数之和 : " + stats.getSum());
System.out.println("平均数 : " + stats.getAverage());

anyMatch,allMatch,noneMatch,count
  1. anyMatch表示,判断的条件里,任意一个元素成功,返回true
  2. allMatch表示,判断条件里的元素,所有的都是,返回true
  3. noneMatch跟allMatch相反,判断条件里的元素,所有的都不是,返回true
  4. count:统计结果流中的对象个数
List strs = Arrays.asList("a", "a", "a", "a", "b");

boolean aa = strs.stream().anyMatch(str -> str.equals("a"));
boolean bb = strs.stream().allMatch(str -> str.equals("a"));
boolean cc = strs.stream().noneMatch(str -> str.equals("a"));

long count = strs.stream().filter(str -> str.equals("a")).count();

System.out.println(aa);// TRUE
System.out.println(bb);// FALSE
System.out.println(cc);// FALSE
System.out.println(count);// 

Collectors.groupingBy

顾名思义就是将流中的对象进行分组,返回一个Map集合.其中可以根据需要来选择key 和value.示例如下所示:

Map map = Map.of(1,23,12,11);


List list = List.of(new Student("1",132),
               new Student("1",131));


Map result = list.parallelStream().collect(Collectors.groupingBy(e->e.name
               ,Collectors.mapping(Student::age,Collectors.toSet())));
       


//如此这般,result 的key就是student的name , vlaue就是student的set结合包含了age
System.out.println(result);



//studeng的声明如下所示

 public record Student(String name,int age){

    }

如果多线程的话可以使用 groupingByConcurrent 接收子线程的结果,以并发执行的方式,而不需要一个一个接收子线程的结果,以此来提升工作效率cuiyaonan2000@163.com

Collectors.toMap

同理toMap也提供了多种方式应对各种场景,所以自定义的Collectors是否有必要,另外这里要注意的是如果key有重复的 且没有指明处理方式的时候会报错,但是如果指明了当key如何处理value的函数(即下方的 (s,a)-> Integer.valueOf((s+a)) ,则不会报错cuiyaonan2000@163.com

 public static void main(String[] args){

       List list = List.of(new Student("1",132),
               new Student("1",131));


        result = list.parallelStream().collect(Collectors.toMap(Student::name,Student::age,
                (s,a)-> Integer.valueOf((s+a))));

        //这样子如果key相同的话 就会累加age 并返回
        System.out.println(result);
    }


    public record Student(String name,int age){

    }

同理也提供了以并行的方式收集子线程结果的方法

 

Collectors.mapping
Collectors.mapping(Student::age,Collectors.toSet())

Adapts a Collector accepting elements of type U to one accepting elements of type T by applying a mapping function to each input element before accumulation.

Params: mapper – a function to be applied to the input elements

downstream – a collector which will accept mapped values

Returns: a collector which applies the mapping function to the input elements and provides the mapped results to the downstream collector

API Note:The mapping() collectors are most useful when used in a multi-level reduction, such as downstream of a groupingBy or partitioningBy.

For example, given a stream of Person, to accumulate the set of last names in each city:

 Map lastNamesByCity    = people.stream().collect(      groupingBy(Person::getCity,                 mapping(Person::getLastName,                         toSet())));

如上 Collector.mapping 跟我们使用map()方法一样就是遍历一个对象,然后返还一个对象.上面说的是非常适合跟groupby配合使用的.

//Map map = Map.of(1,23,12,11);

List list = List.of(new Student("1",132),
               new Student("1",131));

//list.stream().collect(Collectors.toList());

Map result = list.parallelStream().collect(Collectors.groupingBy(e->e.name
               ,Collectors.mapping(student -> student,Collectors.toMap(Student::name,Student::age))));
Collectors.partitioningBy

JDK自己提供的Collector 很多都可以是设置 结果集合的类型,当然了这里我们自己提供的集合类型可以是Map,List,Set,也可以使用JDK自带的Collector中的toList(),toSet(),toMap()等集合cuiyaonan2000@163.com  这里的partitionBy也不例外

 

partitioningBy 在遍历集合的时候根据断言 判断是放到Map的true 或者 false 的key对应的结合当中.

结果的格式为: Map

List list = List.of(new Student("1",132),
               new Student("1",131));

Map tempResult = null;

tempResult = list.stream().collect(Collectors.partitioningBy(e->e.age>1, Collectors.toList()));

 

collectingAndThen

顾明思议就是先把结果收集后 在进行处理. 

方法的定义如下所示:

 /* @param  the type of the input elements
     * @param  intermediate accumulation type of the downstream collector
     * @param  result type of the downstream collector
     * @param  result type of the resulting collector
     * @param downstream a collector
     * @param finisher a function to be applied to the final result of the downstream collector
     * @return a collector which performs the action of the downstream collector,
     * followed by an additional finishing step
     */


    public static Collector collectingAndThen(Collector downstream,
                                                                Function finisher) 

完整实例
import java.util.ArrayList;
import java.util.Arrays;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.Map;
 
public class Java8Tester {
   public static void main(String args[]){
      System.out.println("使用 Java 7: ");
        
      // 计算空字符串
      List strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
      System.out.println("列表: " +strings);
      long count = getCountEmptyStringUsingJava7(strings);
        
      System.out.println("空字符数量为: " + count);
      count = getCountLength3UsingJava7(strings);
        
      System.out.println("字符串长度为 3 的数量为: " + count);
        
      // 删除空字符串
      List filtered = deleteEmptyStringsUsingJava7(strings);
      System.out.println("筛选后的列表: " + filtered);
        
      // 删除空字符串,并使用逗号把它们合并起来
      String mergedString = getMergedStringUsingJava7(strings,", ");
      System.out.println("合并字符串: " + mergedString);
      List numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
        
      // 获取列表元素平方数
      List squaresList = getSquares(numbers);
      System.out.println("平方数列表: " + squaresList);
      List integers = Arrays.asList(1,2,13,4,15,6,17,8,19);
        
      System.out.println("列表: " +integers);
      System.out.println("列表中最大的数 : " + getMax(integers));
      System.out.println("列表中最小的数 : " + getMin(integers));
      System.out.println("所有数之和 : " + getSum(integers));
      System.out.println("平均数 : " + getAverage(integers));
      System.out.println("随机数: ");
        
      // 输出10个随机数
      Random random = new Random();
        
      for(int i=0; i < 10; i++){
         System.out.println(random.nextInt());
      }
        
      System.out.println("使用 Java 8: ");
      System.out.println("列表: " +strings);
        
      count = strings.stream().filter(string->string.isEmpty()).count();
      System.out.println("空字符串数量为: " + count);
        
      count = strings.stream().filter(string -> string.length() == 3).count();
      System.out.println("字符串长度为 3 的数量为: " + count);
        
      filtered = strings.stream().filter(string ->!string.isEmpty()).collect(Collectors.toList());
      System.out.println("筛选后的列表: " + filtered);
        
      mergedString = strings.stream().filter(string ->!string.isEmpty()).collect(Collectors.joining(", "));
      System.out.println("合并字符串: " + mergedString);
        
      squaresList = numbers.stream().map( i ->i*i).distinct().collect(Collectors.toList());
      System.out.println("Squares List: " + squaresList);
      System.out.println("列表: " +integers);
        
      IntSummaryStatistics stats = integers.stream().mapToInt((x) ->x).summaryStatistics();
        
      System.out.println("列表中最大的数 : " + stats.getMax());
      System.out.println("列表中最小的数 : " + stats.getMin());
      System.out.println("所有数之和 : " + stats.getSum());
      System.out.println("平均数 : " + stats.getAverage());
      System.out.println("随机数: ");
        
      random.ints().limit(10).sorted().forEach(System.out::println);
        
      // 并行处理
      count = strings.parallelStream().filter(string -> string.isEmpty()).count();
      System.out.println("空字符串的数量为: " + count);
   }
    
   private static int getCountEmptyStringUsingJava7(List strings){
      int count = 0;
        
      for(String string: strings){
        
         if(string.isEmpty()){
            count++;
         }
      }
      return count;
   }
    
   private static int getCountLength3UsingJava7(List strings){
      int count = 0;
        
      for(String string: strings){
        
         if(string.length() == 3){
            count++;
         }
      }
      return count;
   }
    
   private static List deleteEmptyStringsUsingJava7(List strings){
      List filteredList = new ArrayList();
        
      for(String string: strings){
        
         if(!string.isEmpty()){
             filteredList.add(string);
         }
      }
      return filteredList;
   }
    
   private static String getMergedStringUsingJava7(List strings, String separator){
      StringBuilder stringBuilder = new StringBuilder();
        
      for(String string: strings){
        
         if(!string.isEmpty()){
            stringBuilder.append(string);
            stringBuilder.append(separator);
         }
      }
      String mergedString = stringBuilder.toString();
      return mergedString.substring(0, mergedString.length()-2);
   }
    
   private static List getSquares(List numbers){
      List squaresList = new ArrayList();
        
      for(Integer number: numbers){
         Integer square = new Integer(number.intValue() * number.intValue());
            
         if(!squaresList.contains(square)){
            squaresList.add(square);
         }
      }
      return squaresList;
   }
    
   private static int getMax(List numbers){
      int max = numbers.get(0);
        
      for(int i=1;i < numbers.size();i++){
        
         Integer number = numbers.get(i);
            
         if(number.intValue() > max){
            max = number.intValue();
         }
      }
      return max;
   }
    
   private static int getMin(List numbers){
      int min = numbers.get(0);
        
      for(int i=1;i < numbers.size();i++){
         Integer number = numbers.get(i);
        
         if(number.intValue() < min){
            min = number.intValue();
         }
      }
      return min;
   }
    
   private static int getSum(List numbers){
      int sum = (int)(numbers.get(0));
        
      for(int i=1;i < numbers.size();i++){
         sum += (int)numbers.get(i);
      }
      return sum;
   }
    
   private static int getAverage(List numbers){
      return getSum(numbers) / numbers.size();
   }
}

返回的内容

$ javac Java8Tester.java 
$ java Java8Tester
使用 Java 7: 
列表: [abc, , bc, efg, abcd, , jkl]
空字符数量为: 2
字符串长度为 3 的数量为: 3
筛选后的列表: [abc, bc, efg, abcd, jkl]
合并字符串: abc, bc, efg, abcd, jkl
平方数列表: [9, 4, 49, 25]
列表: [1, 2, 13, 4, 15, 6, 17, 8, 19]
列表中最大的数 : 19
列表中最小的数 : 1
所有数之和 : 85
平均数 : 9
随机数: 
-393170844
-963842252
447036679
-1043163142
-881079698
221586850
-1101570113
576190039
-1045184578
1647841045
使用 Java 8: 
列表: [abc, , bc, efg, abcd, , jkl]
空字符串数量为: 2
字符串长度为 3 的数量为: 3
筛选后的列表: [abc, bc, efg, abcd, jkl]
合并字符串: abc, bc, efg, abcd, jkl
Squares List: [9, 4, 49, 25]
列表: [1, 2, 13, 4, 15, 6, 17, 8, 19]
列表中最大的数 : 19
列表中最小的数 : 1
所有数之和 : 85
平均数 : 9.444444444444445
随机数: 
-1743813696
-1301974944
-1299484995
-779981186
136544902
555792023
1243315896
1264920849
1472077135
1706423674
空字符串的数量为: 2

关注
打赏
1638267374
查看更多评论
立即登录/注册

微信扫码登录

0.0505s