【Java】Stream API

概述

Stream API ( java.util.stream) 把真正的函数式编程风格引入到Java中。这是目前为止对Java类库最好的补充,因为Stream API可以极大提供Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。

Stream是Java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API对集合数据进行操作,就类似于使用SQL执行的数据库查询。 也可以使用Stream API来并行执行操作。简言之,Stream API提供了一种高效且易于使用的处理数据的方式。

实际开发中,项目中多数数据源都来自于Mysql,Oracle等。但现在数据源可以更多了,有MongDB,Radis等,而这些NoSQL的数据就需要Java层面去处理。

Stream和 Collection集合的区别:Collection是一种静态的内存数据结构,而 Stream是有关计算的。前者是主要面向内存,存储在内存中, 后者主要是面向 CPU,通过CPU 实现计算。

总之,Stream到底是一种数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,Stream讲的是计算!”

注意:

  • Stream自己不会存储元素。
  • Stream不会改变源对象。相反,他们会返回一个持有结果的新Stream。
  • Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

三个步骤

Stream操作分为三个步骤:

  1. 创建Stream
  2. 中间操作
  3. 终止操作

创建Stream

方式一:Java8中的Collection接口被扩展,提供了两个获取流的方法

  • default Stream stream() : 返回一个顺序流
  • default Stream parallelStream() : 返回一个并行流
List<Employee> employees = EmployeeData.getEmployees();

// default Stream<E> stream() : 返回一个顺序流
Stream<Employee> stream = employees.stream();

//default Stream<E> parallelStream() : 返回一个并行流
Stream<Employee> parallelStream = employees.parallelStream();

方式二:Java8中的Arrays的静态方法stream()可以获取数组流

static Stream stream(T[] array): 返回一个流

重载形式,能够处理对应基本类型的数组:

public static IntStream stream(int[] array)
public static LongStream stream(long[] array)
public static DoubleStream stream(double[] array)
int[] arr = new int[]{1, 3, 4, 5, 5};
IntStream stream = Arrays.stream(arr);

Employee[] employees = new Employee[]{new Employee(222, "Tom"), new Employee(33)};
Stream<Employee> stream1 = Arrays.stream(employees);

方式三:通过Streamof()

可以调用Stream类静态方法of(), 通过显示值创建一个流。它可以接收任意数量的参数。

public static Stream of(T... values) : 返回一个流

Stream<Integer> stream = Stream.of(1, 3, 4, 6, 4);

中间操作

多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。

筛选与切片

方法描述
filter(Predicate p)接收Lambda,从流中排除某些元素
distinct()筛选,通过流所生成元素的hashCode()equals()去除重复元素
limit(Iong maxSize)截断流,使其元素不超过给定数量
skip(long n)跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n)互补

示例代码:

// filter: 查询员工表中薪资大于7000的员工信息
list.stream().filter(employee -> employee.getSalary() > 7000).forEach(System.out::println);

System.out.println();

// limit: 查询前3条记录
list.stream().limit(3).forEach(System.out::println);

System.out.println();

// skip: 跳过前3条记录
list.stream().skip(3).forEach(System.out::println);

System.out.println();

// distinct: 筛选
list.add(new Employee(1002, "马云", 12, 9876.12));
list.add(new Employee(1002, "马云", 12, 9876.12));
list.add(new Employee(1002, "马云", 12, 9876.12));
list.add(new Employee(1002, "马云", 12, 9876.12));

for (Employee employee : list) {
    System.out.println(employee);
}

System.out.println();

list.stream().distinct().forEach(System.out::println);

映射

map(Function f):接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。

示例代码:

// 获取员工姓名长度大于3的员工的姓名
// 先映射,后筛选,lambda写法
list.stream().map(employee -> employee.getName()).filter(str -> str.length() > 3).forEach(System.out::println);
// 先映射,后筛选,方法引用写法
list.stream().map(Employee::getName).filter(str -> str.length() > 3).forEach(System.out::println);
// 先筛选,后映射,lambda写法
list.stream().filter(employee -> employee.getName().length() > 3).map(employee -> employee.getName()).forEach(System.out::println);
// 先筛选,后映射,方法引用写法
list.stream().filter(employee -> employee.getName().length() > 3).map(Employee::getName).forEach(System.out::println);

排序

sorted():产生一个新流,其中按自然顺序排序

sorted(Comparator com):产生一个新流,其中按比较器顺序排序

示例代码:

// 新定义一个list进行自然排序
List<Integer> list1 = Arrays.asList(12, 33, 35, 32, 22, 11, 55, 33);
list1.stream().sorted().forEach(System.out::println);

// 按员工年龄升序排序
list.stream().sorted((e1, e2) -> e1.getAge() - e2.getAge()).forEach(System.out::println);

终止操作

一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用。注意:流进行了终止操作后,不能再次使用。

匹配与查找

方法描述
allMatch(Predicate p)检查是否匹配所有元素
anyMatch(Predicate p)检查是否至少匹配一个元素
noneMatch(Predicate p)检查是否没有匹配所有元素
findFirst()返回第一个元素
findAny()返回当前流中的任意元素
count()返回流中元素总数
max(Comparator c)返回流中最大值
min(Comparator c)返回流中最小值
forEach(Consumer c)内部迭代

示例代码:

// 是否所有员工年龄都大于18
boolean allMatch = list.stream().allMatch(employee -> employee.getAge() > 18);
System.out.println(allMatch);

// 是否存在员工的薪水大于10000
boolean anyMatch = list.stream().anyMatch(employee -> employee.getSalary() > 10000);
System.out.println(anyMatch);

// 返回最高的工资
Optional<Double> maxSalary = list.stream().map(Employee::getSalary).max(Double::compareTo);
System.out.println(maxSalary.get());

// 返回最低工资的员工
Optional<Employee> employee = list.stream().min((emp1, emp2) -> Double.compare(emp1.getSalary(), emp2.getSalary()));
System.out.println(employee.get());

规约

reduce(T iden, BinaryOperator b):可以将流中元素反复结合起来,得到一个值。返回Tiden可以理解为初始值。

reduce(BinaryOperator b):可以将流中元素反复结合起来,得到一个值。返回Optional<T>

备注:mapreduce的连接通常称为map-reduce模式,因Google用它来进行网络搜索而出名。

示例代码:

// 计算所有员工工资总和
Double totalSalary1 = list.stream().map(Employee::getSalary).reduce(0.0, (d1, d2) -> d1 + d2);
// 注意这种方式返回值类型会有Optional类包裹
Optional<Double> totalSalary2 = list.stream().map(Employee::getSalary).reduce(Double::sum);
System.out.println(totalSalary1);
System.out.println(totalSalary2.get());

收集

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

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

方法返回类型作用
toListList<T>把流中元素收集到List
toSetSet<T>把流中元素收集到Set
toCollectionCollection<T>把流中元素收集到创建的集合

例如:

list.stream().collect(Collections.toList())

list.stream().collect(Collections.toSet())

list.stream().collect(Collections.toCollection(ArrayList::new))

示例代码:

// 查找工资大于6000的员工,结果返回为list
List<Employee> empList = list.stream().filter(emp -> emp.getSalary() > 6000).collect(Collectors.toList());
empList.forEach(System.out::println);
// 查找工资大于6000的员工,结果返回为set
Set<Employee> empSet = list.stream().filter(emp -> emp.getSalary() > 6000).collect(Collectors.toSet());
empSet.forEach(System.out::println);

Employee类

public class Employee {
    private int id;
    private String name;
    private int age;
    private double salary;

    public Employee() {
    }

    public Employee(int id, String name, int age, double salary) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Employee employee = (Employee) o;
        return id == employee.id && age == employee.age && Double.compare(employee.salary, salary) == 0 && Objects.equals(name, employee.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name, age, salary);
    }

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", salary=" + salary +
                '}';
    }
}

以及随便创建的list数据

static ArrayList<Employee> list = new ArrayList<>();

static {
    list.add(new Employee(1001, "马化腾", 34, 6000.38));
    list.add(new Employee(1002, "马云", 2, 9876.12));
    list.add(new Employee(1003, "刘强东", 33, 3000.82));
    list.add(new Employee(1004, "雷军", 26, 7657.37));
    list.add(new Employee(1005, "李彦宏", 65, 5555.32));
    list.add(new Employee(1006, "比尔盖茨", 42, 9500.43));
    list.add(new Employee(1007, "任正非", 26, 4333.33));
    list.add(new Employee(1008, "扎克伯格", 35, 2500.32));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

h0l10w

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值