关于Java8Stream流的使用

·  阅读 840

Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据,将要处理的元素集合看作一种流

流在管道中传输,并且可以在管道的节点上进行处理,比如筛选、排序、聚合等,在管道中经过中间操作的处理后,最后由最终操作得到前面处理的结果。

总而言之,流的使用一般包括三件事:

一个数据源(如集合)来执行一个查询;

一个中间操作链,形成一条流的流水线;

一个终端操作,执行流水线,并生成结果。

中间操作,比如

filter() 、 map() 、sorted()、skip()、peek

终端操作,比如

allMatch()、anyMatch()、noneMatch()、forEach()、collect()、count()

流有两大特点,流水线(流水线的操作可以看作对数据源进行数据库式查询)和内部迭代,使用Stream API 可以写出更简洁、易读、灵活和性能更好的代码

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

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

通过简单的例子熟悉stream的使用

创建一个User类并进行各种stream流操作

@Data
@AllArgsConstructor
public class User {

    public Long id;

    private String name;

    private Integer age;

    private Double grades;

    private String sex;

    /**
     * 季度平时分
     */
    private String usualGrades;

    }
复制代码

流操作

// 查询成绩及格的用户的姓名
List<String> nameList1 = users.stream().filter(x -> x.getGrades() > 60).map(User::getName).collect(Collectors.toList());


// 查询成绩前三(成绩相同,id较小的靠前)的姓名
List<String> nameList2 = users.stream().sorted(Comparator.comparingDouble(User::getGrades).reversed().thenComparingLong(User::getId))
        .limit(3).map(User::getName).collect(Collectors.toList());


// 统计男生数量
long num = users.stream().filter(x -> x.getSex().equals("男")).count();

//统计全班的平时分总和
Integer collect = users.stream().map(x -> x.getUsualGrades().split(",")).flatMap(Arrays::stream).collect(Collectors.summingInt(x -> Integer.parseInt(x)));


// 查看班上是否一位叫小明的同学
boolean flag = users.stream().anyMatch(x -> x.getName().equals("小明"));


// 查看全班同学是否都以"小"开头
boolean flag2 = users.stream().allMatch(x -> x.getName().startsWith("小"));

//  打印所有信息
 users.stream().forEach(System.out::println);


复制代码

通过上述操作,也简单了解一些流的操作

  • filter 接受Lambda,从流中排除某些元素或者提取某些元素
  • map 接受一个Lambda,将元素转换成其他形式或提取元素
  • collect 将流转换为其他形式
  • sorted 将某些元素进行排序
  • limit 截断流,使其元素不超过给定数量
  • flatMap 将每个映射流的内容分裂成多个同样的流(一转多,比如:上述实例,每个同学有一个季度平时分【有三个分组成】,现在把他们都提取出来,变成一个个的分数)
  • anyMatch 匹配上任何一个则返回 Boolean
  • allMatch 匹配所有的元素则返回 Boolean
  • findAny 查找任何一个就返回 Optional
  • findFirst 查找到第一个就返回 Optional
  • forEach 遍历所有信息

还有挺多功能强大的流操作值得我们去发现,在上述操作,发现大部分操作都是接受一个Laombda表达式的语句,点击进入看源码我们能发现传入的是一个函数式接口,比如filter,传入是Predicate接口,其中只有一个方法test,传入一个参数,返回boolean类型

函数式接口

一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口,一般通过FunctionalInterface这个注解来表明某个接口是一个函数式接口,可以被隐式转换为 lambda 表达式。

常用的函数式接口:

  • Consumer 顾名思义,Consumer的意思就是消费,默认方法是 void accept(T t)【代表了接受一个输入参数并且无返回的操作】,流操作forEach() 就用到
  • Function 功能或者函数 ,默认方法是 R apply(T t)【接受一个输入参数,返回一个结果】 ,在流操作map()用到
  • Predicate 断言,默认方法是boolean test(T t)【接受一个输入参数,返回一个布尔值结果】,流操作filter() 用到
  • Supplier 提供、供应,默认方法是T get()【无参数,返回一个结果】
  • BiConsumer<T,U> 代表了一个接受两个输入参数的操作,并且不返回任何结果
  • BiFunction<T,U,R> 代表了一个接受两个输入参数的方法,并且返回一个结果
  • IntConsumer 接受一个int类型的输入参数,无返回值
  • IntToDoubleFunction 接受一个int类型输入,返回一个double类型结果
  • ToIntFunction 接受一个输入参数,返回一个int类型结果。
分类:
后端
标签: