Java教程

Stream流式计算

本文主要是介绍Stream流式计算,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

Stream流式计算

    • 什么是Stream
    • Stream操作类别
      • 中间操作
      • 终端操作
    • Stream的特性
    • Stream的创建
      • 集合创建流
      • 数组创建流
      • 静态方法创建
        • Stream.of()
        • Stream.generate()
        • Stream.iterate()
      • stream的使用
      • 进行终端操作
      • 进行中间操作
      • 收集(collect)

在这里插入图片描述

什么是Stream

将对要处理的集合当做数据源,看作一种数据流,在流的过程中,借助stream的API对流中的元素进行处理:过滤,排序等;

Stream操作类别

中间操作

每次返回新的流,可以多次调用中间操作。中间操作不会对源数据产生影响。

终端操作

每个流只会进行一次终端操作,终端操作后流就无法使用了。终端操作结束后会产生新的集合或值。

Stream的特性

Stream不存储数据,只对数据按照一定的操作进行处理,再输出结果;
Stream不改变数据集,终端操作后生成新的数据集或值;
Stream具有延迟性,只有调用终端操作时,中间操作才会进行。

Stream的创建

集合创建流

List<String> list = Arrays.asList("a","b","c");
// 创建顺序流
Stream<String> stream = list.stream();
// 创建并行流
Stream<String> stringStream = list.parallelStream();

顺序流与顺序流的简单区别

stream() 顺序流:主线程对多个流进行顺序执行

parallelStream() 并发流:内部以多线程的形式对流进行并发执行

image

当数据量足够大的情况下,并发流可以加快速度,但是前提一定是流中的数据没有顺序要求。

除了直接创建并发流以外,还提供了把顺序流转为并发流:

Stream<String> parallel = list.stream().parallel();

数组创建流

String[] strings = {"1","2","3","4"};
Stream<String> stream = Arrays.stream(strings);

int[] array = {1, 2, 3};
// 数组创建流
IntStream stream = Arrays.stream(array);

静态方法创建

Stream.of()

 Stream<String> stream = Stream.of("1","2","3");

Stream.generate()

Stream<String> stream = Stream.generate(() ->{return UUID.randomUUID();
        });

generate()里面的参数是一个Supplier(供给型)类型的函数式接口

Stream.iterate()

生成无限长度的Stream,和generator不同的是,其元素的生成是重复对给定的种子值(seed)调用用户指定函数来生成的

 Stream.iterate(0, (x) -> {
            return x + 3;
        }).limit(5).forEach((integer) ->{
            System.out.println(integer);
        }); //结果是0,3,6,9,12

stream的使用

先创建一个数据源实体类

@Data
public class Person {
    /**
     * 姓名
     */
    private String name;
    /**
     * 薪资
     */
    private int salary;
    /**
     * 年龄
     */
    private int age;
    /**
     * 性别
     */
    private String sex;
    /**
     * 地区
     */
    private String area;

    public Person(String name, int salary, int age, String sex, String area) {
        this.name = name;
        this.salary = salary;
        this.age = age;
        this.sex = sex;
        this.area = area;
    }
}

创建数据源

        List<Person> list = new ArrayList<>();
        list.add(new Person("Tom", 8900, 25,"male", "New York"));
        list.add(new Person("Jack", 7000, 26,"male", "Washington"));
        list.add(new Person("Lily", 7800, 27,"female", "Washington"));
        list.add(new Person("Anni", 8200, 28,"female", "New York"));
        list.add(new Person("Owen", 9500, 29,"male", "New York"));
        list.add(new Person("Alisa", 7900, 30,"female", "New York"));

进行终端操作

  1. 遍历/匹配(foreach/find/match)
//遍历每一个元素,并打印元素的姓名
list.stream().forEach(person -> {
          System.out.println(person.getName());
      });
//查找到第一个,并打印出他的姓名
Optional<Person> first = list.stream().findFirst(); //findAny是查找到一个立马返回查到的,适用于并发流
System.out.println(first.get().getName());

//是否匹配到,任意匹配
boolean jack = list.stream().anyMatch(person -> {
          return person.getName().equals("Jack");
      });
System.out.println(jack);
Tom
Jack
Lily
Anni
Owen
Alisa
Tom
Tom
true
  1. 聚合(max/min/count)
//查出年龄最大的那个人
Optional<Person> max = list.stream().max(((o1, o2) -> {
            return o1.getAge() - o2.getAge();
        }));
System.out.println("年龄最大的是:"+max.get().getName()+"他的年龄是:"+max.get().getAge());

//查出年龄最小的那个人,需要注意的比较器的方法还是一样的  o1.getAge() - o2.getAge()
Optional<Person> min = list.stream().min((o1, o2) -> {
             return o1.getAge() - o2.getAge();
        });
System.out.println("年龄最小的是:"+min.get().getName()+"他的年龄是:"+min.get().getAge());

//统计数据源的元素个数
long count = list.stream().count();
System.out.println(count);
//统计工资高于8000的元素个数
 long count1 = list.stream().filter((person) -> {
            return person.getSalary() > 8000;
        }).count();
System.out.println(count1);
年龄最大的是:Alisa他的年龄是:30
年龄最小的是:Tom他的年龄是:25
6
3
  1. 归约(reduce)
    规约,也称缩减。顾名思义,就是将一个流缩减为一个值,实现对一个集合求和、求积的聚合计算
Optional<Integer> reduce = list.stream().map((person) -> {
            return person.getSalary();
        }).reduce((integer1, integer2) -> {
            return integer1 + integer2;
        });
System.out.println(reduce.get());
49300

map操作里面接收的是一个功能性函数式接口,接收一个类型值,返回另一个类型的,
比如接收了Person,返回的是薪水person.getSalary()。然后将这些薪水当成新的数据源再进行reduce操作或其他操作。



进行中间操作

  1. 筛选(filter)
//筛选出年龄大于26的并统计人数
long count2 = list.stream().filter((person -> {
            return person.getAge() > 26;
        })).count();
System.out.println(count2);
  1. 映射(map/flatMap)
    映射:将流中的元素按照一定的规则映射到另一个流中;
    map:参数为一个功能型函数式接口,该函数会应用到流中的每一个元素,并将其映射成一个新元素;
    flatMap:参数为一个函数,函数将流中的每一个元素映射成一个新的流,并将所有转化的流连成一个新的流;
//将元素的姓名收集到一个新的集合中并打印出来
List<String> names = new ArrayList<>();
names = list.stream().map(person -> {return person.getName();}).collect(Collectors.toList());
System.out.println(names);

flatMap操作使用里一个例子

String[] words = new String[]{"Hello", "World"};
Gson gson = new Gson();
//这是map操作
List<String[]> mapList = Stream.of(words).map(w -> w.split("")).distinct().collect(Collectors.toList());
System.out.println(gson.toJson(mapList));
结果:[["H","e","l","l","o"],["W","o","r","l","d"]]
//这是flatMap操作
List<String> flatMapCollect = Stream.of(words).flatMap(w -> Stream.of(w.split(""))).distinct().collect(Collectors.toList());
System.out.println(gson.toJson(flatMapCollect));
结果:["H","e","l","o","W","r","d"]

收集(collect)

collect,收集是内容最多,功能最丰富的部分。字面理解就是将一个流收集起来,最终可以收集成一个值也可以是一个新的集合。主要依赖java.util.stream.Collectors类内置的静态方法。

  • 归集(toList/toSet/toMap)
    因为流不存储数据,所以流处理完成后,需要一个容器去收集。toList、toSet和toMap比较常用,另外还有toCollection、toConcurrentMap等复杂一些的用法。


    toList:

          List<String> names = new ArrayList<>();
          names = list.stream().map(person -> {return person.getName();}).collect(Collectors.toList()); //将姓名重新收集到一个List中去
          System.out.println(names);
    
      [Tom, Jack, Lily, Anni, Owen, Alisa]
    

    toMap:

    Map<String, String> map = list.stream().collect(Collectors.toMap(person -> person.getName(), person -> person.getArea())); //以姓名为key,地址为value的map
    System.out.println(map);
    
    {Tom=New York, Owen=New York, Anni=New York, Alisa=New York, Jack=Washington, Lily=Washington}
    

    toSet:

    Set<Person> set = list.stream().collect(Collectors.toSet());//可以直接收集
    System.out.println(set);
    
    [Person(name=Anni, salary=8200, age=28, sex=female, area=New York), 
     Person(name=Lily, salary=7800, age=27, sex=female, area=Washington), 
     Person(name=Tom, salary=8900, age=25, sex=male, area=New York), 
     Person(name=Jack, salary=7000, age=26, sex=male, area=Washington), 
     Person(name=Owen, salary=9500, age=29, sex=male, area=New York), 
     Person(name=Alisa, salary=7900, age=30, sex=female, area=New York)]
    
    


    • 统计(count/averaging)
      count:
      count之前用过很多次了,就是用来计数啥的,比如统计员工数、加上中间操作map,统计工资总计等等;


      averaging:
      平均值,平均值还有averagingLong、averagingDouble等

      //统计平均工资
      Double averagSalary = list.stream().collect(Collectors.averagingInt(person -> person.getSalary()));
      System.out.println(averagSalary);
      
      8216.666666666666
      

    • 最值(maxBy\minBy)

      maxBy:

      //注意返回的是Person类型的Optional
      Optional<Person> maxSalary = list.stream().collect(Collectors.maxBy((o1, o2) -> o1.getSalary() - o2.getSalary()));
       System.out.println(maxSalary.get().getSalary());
      
      9500
      

      minBy:
      和maxBy一样的。

    • 求和(summingInt\summingLong\summingDouble)
      summingInt:

      //很简单的操作
      Integer sumSalary = list.stream().collect(Collectors.summingInt(person -> person.getSalary()));
      System.out.println(sumSalary);
      

      summarizingInt:
      summarizingInt是所有维度汇总,就是总共几个人,最大,最小,平均啥的,

      IntSummaryStatistics sumSalary = list.stream().collect(Collectors.summarizingInt(person -> 
                                       person.getSalary()));
      System.out.println(sumSalary);
      
      IntSummaryStatistics{count=6, sum=49300, min=7000, average=8216.666667, max=9500}
      

    • 分组(partitioningBy/groupingBy)
      partitioningBy:
      方法参数为Predicate接口,该接口返回值是boolean类型,所以partitioningBy也只能将集合按true/false分为两组。

      //按照工资是否大于8000分为两组
      Map<Boolean, List<Person>> booleanListMap = list.stream().collect(Collectors.partitioningBy(person -> person.getSalary() > 8000));
      System.out.println(booleanListMap);
      
      {
      false=[Person(name=Jack, salary=7000, age=26, sex=male, area=Washington), 
                Person(name=Lily, salary=7800, age=27, sex=female, area=Washington), 
               Person(name=Alisa, salary=7900, age=30, sex=female, area=New York)], 
      true=[Person(name=Tom, salary=8900, age=25, sex=male, area=New York), 
               Person(name=Anni, salary=8200, age=28, sex=female, area=New York), 
               Person(name=Owen, salary=9500, age=29, sex=male, area=New York)]
      }
      

      groupingBy:
      方法参数为Function接口,key是泛型,所以groupingBy可以将集合分为多组。

      //薪水为key,也可以以姓名为key,那就是person.getName(),map的value是类型为Person的List
      Map<Integer, List<Person>> collect = list.stream().collect(Collectors.groupingBy(person -> person.getSalary()));
      System.out.println(collect);
      
      {
      8900=[Person(name=Tom, salary=8900, age=25, sex=male, area=New York)], 
      8200=[Person(name=Anni, salary=8200, age=28, sex=female, area=New York)], 
      7800=[Person(name=Lily, salary=7800, age=27, sex=female, area=Washington)], 
      7000=[Person(name=Jack, salary=7000, age=26, sex=male, area=Washington)], 
      7900=[Person(name=Alisa, salary=7900, age=30, sex=female, area=New York)], 
      9500=[Person(name=Owen, salary=9500, age=29, sex=male, area=New York)]
      }
      

    • 接合(joining)
      joining可以将stream中的元素用特定的连接符(没有的话,则直接连接)连接成一个字符串。

      //我试了几次,String才能join
       String str = list.stream().map(person -> person.getName()).collect(Collectors.joining(","));
       System.out.println(str);
      
      Tom,Jack,Lily,Anni,Owen,Alisa
      

    • 归约(reducing)
      Collectors类提供的reducing方法,相比于stream本身的reduce方法,增加了对自定义归约的支持。

    // 汇总扣除起征点后工资(Collectors类提供的reducing)
    Integer integer = list.stream().collect(Collectors.reducing(0, person -> person.getSalary(), (integer1, integer2) -> integer1 + integer2 - 5000));
    System.out.println(integer);
    

    说明一下:integer1一开始是第一个参数的0,integer2 是第一个元素的薪水8900,0 + 8900 - 5000 = 3900,这个运算结果就是下一次的integer1的值。

    19300
    

    • 排序(sorted)
      sorted():自然排序,流中元素类型需实现Comparable接口;
      sorted(Comparator com):Comparator排序器自定义排序;
      自然排序:

      // 工资顺序排序
      List<String> salaryAse = list.stream()
      .sorted(Comparator.comparing(person -> person.getSalary()))
      .map(person -> person.getName()).collect(Collectors.toList());
       System.out.println(salaryAse);
      // 工资倒序排序
      List<String> salaryDesc = list.stream()
      .sorted(Comparator.comparing((Person person) -> person.getSalary()).reversed())
      .map(person -> person.getName()).collect(Collectors.toList());
      System.out.println(salaryAse);
      
      [Jack, Lily, Alisa, Anni, Tom, Owen]
      [Owen, Tom, Anni, Alisa, Lily, Jack]
      

      注意:Comparator.comparing((Person person) -> person.getSalary()).reversed(),要显示注明类型,不然编译推断不出类型就当做Object了。


      排序是对null值的处理:

      // 工资顺序排序,空值在前
      List<String> nullFirst = list.stream().sorted(Comparator.comparing(person -> person.getSalary() ,Comparator.nullsFirst((o1, o2) -> o1.compareTo(o2)))).map(person -> person.getName()).collect(Collectors.toList());
      ///工资顺序排序,空值在后
      List<String> nullLast = list.stream().sorted(Comparator.comparing(person -> person.getSalary() ,Comparator.nullsFirst((o1, o2) -> o1.compareTo(o2)))).map(person -> person.getName()).collect(Collectors.toList());
      

      说明:o1 ,o2就是薪水。


      组合排序:

         // 先工资倒序,再年龄正序
         List<String> thenComparingStr = list.stream().sorted(Comparator.comparing((Person person) -> person.getSalary(), Comparator.reverseOrder()).thenComparing(person -> person.getAge(), Comparator.naturalOrder()))
                 .map(person -> person.getName()).collect(Collectors.toList());
         System.out.println(thenComparingStr);
      
      //这里结果和工资倒序的结果是一样的,参考Sql
      [Owen, Tom, Anni, Alisa, Lily, Jack]
      

    • 提取/组合


      去重(distinct):

           // 聚合员工所有地区并去重
          List<String> distinctStr = list.stream().map(person -> person.getArea()).distinct().collect(Collectors.toList());
          System.out.println(distinctStr);
      
          [New York, Washington]
      

      限制(limit):

          // 所有员工按收入高低排序后取前三
          List<String> limitStr = list.stream()
                  .sorted(Comparator.comparing((Person person) -> person.getSalary(), Comparator.reverseOrder())).limit(3)
                  .map(person -> person.getName()).collect(Collectors.toList());
          System.out.println(limitStr);
      
          [Owen, Tom, Anni]
      

      跳过(skip):

          // 所有员工按收入高低排序后跳过最高的
          List<String> skipStr  = list.stream()
                  .sorted(Comparator.comparing((Person person) -> person.getSalary(), Comparator.reverseOrder())).skip(1)
                  .map(person -> person.getName()).collect(Collectors.toList());
          System.out.println(skipStr);
      
         [Tom, Anni, Alisa, Lily, Jack]
      

      skip + limit 实现分页:

          // 分页,从3个数据开始,每页展示2条
          int begin = 2;
          int size = 2;
          List<String> paginationStr = list.stream()
                  .sorted(Comparator.comparing((Person person) -> person.getSalary(), Comparator.reverseOrder())).skip(begin).limit(size)
                  .map(person -> person.getName()).collect(Collectors.toList());
          System.out.println(paginationStr);
      
          [Anni, Alisa]
      
这篇关于Stream流式计算的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!