Java8-Stream API

笔记参考视频:https://www.bilibili.com/video/BV184411x7XA?p=19

什么是Stream

Stream是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。

Stream关注的是对数据的运算

注:

  1. Stream自己不会存储元素
  2. Stream不会改变源对象,而是返回一个持有结果的新Stream
  3. Stream操作是延迟执行的,即会等到需要结果的时候才执行

Stream操作的三个步骤

  1. 创建Stream(实例化)

一个数据源(如:集合、数组),获取一个流

  1. 中间操作(过滤、映射等)

一个中间操作链,对数据源的数据进行处理

  1. 终止操作

一旦执行终止操作,就执行中间操作链,并产生结果。

实体类Employee


public class Employee {

    private int id;
    private String name;
    private int age;
    private double 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;
    }

    public Employee() {
        System.out.println("Employee().....");
    }

    public Employee(int id) {
        this.id = id;
        System.out.println("Employee(int id).....");
    }

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

    public Employee(int id, String name, int age, double salary) {

        this.id = id;
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Employee{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + ", 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;

        if (id != employee.id)
            return false;
        if (age != employee.age)
            return false;
        if (Double.compare(employee.salary, salary) != 0)
            return false;
        return name != null ? name.equals(employee.name) : employee.name == null;
    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        result = id;
        result = 31 * result + (name != null ? name.hashCode() : 0);
        result = 31 * result + age;
        temp = Double.doubleToLongBits(salary);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        return result;
    }
}

生成数据的类


import java.util.ArrayList;
import java.util.List;

public class EmployeeData {

    public static List<Employee> getEmployees(){
        List<Employee> list = new ArrayList<>();

        list.add(new Employee(1001, "马化腾", 34, 6000.38));
        list.add(new Employee(1002, "马云", 12, 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.32));
        list.add(new Employee(1008, "扎克伯格", 35, 2500.32));
        list.add(new Employee(1009, "张三", 26, 6666));
        return list;
    }

}

Stream的实例化

方法1 通过集合创建Stream
@Test
    public void test1(){
        List<Employee> employees = EmployeeData.getEmployees();
        //返回一个顺序流
        Stream<Employee> stream = employees.stream();
        //返回一个并行流
        Stream<Employee> parallelStream = employees.parallelStream();
    }
方法2 通过数组创建Stream
@Test
public void test2(){
    int[] arr = new int[]{1,2,3,4,5,6};
    //调用Arrays类的static<T> Stream<T> stream(T[] array) 返回一个流
    IntStream stream = Arrays.stream(arr);

    Employee e1 = new Employee(1001,"Tom");
    Employee e2 = new Employee(1002,"Sam");
    Employee[] arr1 = new Employee[]{e1,e2};
    Stream<Employee> stream1 = Arrays.stream(arr1);
}
方法3 通过Stream的of()
@Test
public void test3(){
    Stream<Integer> stream = Stream.of(1,2,3,4,5,6);
}
方法4 创建无限流
@Test
public void test4(){
    //迭代
    //public static<T> Stream<T> iterate(final T seed,final UnaryOperator<T> f)
    //遍历前十个偶数
    Stream.iterate(0,t->t+2).limit(10).forEach(System.out::println);

    //生成
    //public static<T> Stream<T> generate(Supplier<T> s)
    Stream.generate(Math::random).limit(10).forEach(System.out::println);
}

Stream的中间操作

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

1.筛选与切片
方法描述
filter(Predicate p)接收Lambda,从流中排除某些元素
distinct()筛选,通过流所生成元素的hashCode()和equals()去除重复元素
limit(long maxSize)截断流,使其元素不超过给定数量
skip(long n)跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit(n)互补
@Test
    public void test1(){
    /**
     *      筛选与切片
     * */
//            * | filter(Predicate p) | 接收Lambda,从流中排除某些元素                               |
    System.out.println("-------------------------------------filter-------------------------------------");
    List<Employee> list = EmployeeData.getEmployees();
    Stream<Employee> stream = list.stream();
    stream.filter(e->e.getSalary()>7000).forEach(System.out::println);
//            * | limit(long maxSize) | 截断流,使其元素不超过给定数量                               |
    System.out.println("-------------------------------------limit-------------------------------------");
    list.stream().limit(3).forEach(System.out::println);
//            * | skip(long n)        | 跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit(n)互补 |
    System.out.println("-------------------------------------skip-------------------------------------");
    list.stream().skip(3).forEach(System.out::println);
//            * | distinct()          | 筛选,通过流所生成元素的hashCode()和equals()去除重复元素     |
    System.out.println("-------------------------------------distinct-------------------------------------");
    list.add(new Employee(1003,"刘强东",33,3000.82));
    list.add(new Employee(1003,"刘强东",33,3000.82));
    list.add(new Employee(1003,"刘强东",33,3000.82));
    list.add(new Employee(1003,"刘强东",33,3000.82));
    list.add(new Employee(1003,"刘强东",33,3000.82));
    System.out.println("筛选前:");
    System.out.println(list);
    System.out.println("筛选后:");
    list.stream().distinct().forEach(System.out::println);

}
2.映射
方法描述
map(Function f)接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
mapToDouble(ToDoubleFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的DoubleStream
mapToInt(ToIntFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的IntStream
mapToLong(ToLongFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的LongStream
flatMap(Function f)接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
@Test
    public void test2(){
    /**
     *          映射
     * */
//          map(Function f)接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
    System.out.println("-------------------------------------map-------------------------------------");
    List<String> list = Arrays.asList("a1", "b2", "c3", "d4", "e5");
    list.stream().map(str->str.toUpperCase()).forEach(System.out::println);

    System.out.println("练习1:获取员工姓名长度大于3的员工的姓名。");
    List<Employee> list0 = EmployeeData.getEmployees();
    //      map
    list0.stream().map(Employee::getName).filter(str->str.length() > 3).forEach(System.out::println);

    System.out.println("练习2");
    System.out.println("-------------------------------------map-------------------------------------");
    Stream<Stream<Character>> streamStream = list.stream().map(StreamAPITest1::fromStringToStream);
    streamStream.forEach(s->{
        s.forEach(System.out::println);
    });
    System.out.println("-------------------------------------flatmap-------------------------------------");
    list.stream().flatMap(StreamAPITest1::fromStringToStream).forEach(System.out::println);

}
//          将字符串中的多个字符构成的集合转换为对应的Stream的实例
    public static Stream<Character> fromStringToStream(String str){
        ArrayList<Character> list = new ArrayList<>();
        for (Character c : str.toCharArray()){
            list.add(c);
        }
        return list.stream();
    }
3.排序
方法描述
sorted()产生一个新流,其中按自然顺序排序
sorted(Comparator com)产生一个新流,其中按比较器顺序排序
@Test
    public void test3(){
        //| sorted()               | 产生一个新流,其中按自然顺序排序   |
        System.out.println("-------------------------------------sorted()自然排序-------------------------------------");

        List<Integer> list = Arrays.asList(11, 22, 33, 44, 88, 21, 2, 53, 6);
        list.stream().sorted().forEach(System.out::println);
        //| sorted(Comparator com) | 产生一个新流,其中按比较器顺序排序 |
        System.out.println("-------------------------------------sorted(Comparator com)定制排序-------------------------------------");
        System.out.println("按年龄排序");
        List<Employee> employeeList = EmployeeData.getEmployees();
        employeeList.stream().sorted((e1,e2)-> Integer.compare(e1.getAge(), e2.getAge())).forEach(System.out::println);
        //年龄相同时按工资排序
        System.out.println("年龄相同时按工资排序");
        employeeList.stream().sorted(
                (e1,e2)->{
                    int temp = Integer.compare(e1.getAge(), e2.getAge());
//                    System.out.println(temp);
//                    System.out.println("000000");
                    if (temp!=0){
                        return temp;
                    }else {
                        return Double.compare(e1.getSalary(),e2.getSalary());
                    }
                }
        ).forEach(System.out::println);
    }

Stream的终止操作

1.匹配与查找
方法描述
allMatch(Predicate p)检查是否匹配所有元素
anyMatch(Predicate p)检查是否匹配至少一个元素
noneMatch(Predicate p)检查是否没有匹配所有元素
findFirst()返回第一个元素
findAny()返回当前流中的任意元素
count()返回流中元素总数
max(Comparator c)返回流中最大值
min(Comparator c)返回流中最小值
forEach(Consumer c)内部迭代(使用Collection接口需要用户做迭代,称为外部迭代。相反,Stream API使用内部迭代)
import org.junit.Test;

import java.util.List;
import java.util.Optional;

public class StreamAPITest2 {
    @Test
    public void test1(){
        /**
         * 匹配与查找
         * */
        //| allMatch(Predicate p)  | 检查是否匹配所有元素     |
        System.out.println("-------------------------------------allMatch-------------------------------------");
        System.out.println("练习1:是否所有的员工年龄都大于18");
        List<Employee> list = EmployeeData.getEmployees();
        boolean result1 = list.stream().allMatch(e -> e.getAge() > 18);
        System.out.println(result1);
        //| anyMatch(Predicate p)  | 检查是否匹配至少一个元素 |
        System.out.println("-------------------------------------anyMatch-------------------------------------");
        System.out.println("练习2:是否存在员工的工资大于4000");
        boolean result2 = list.stream().anyMatch(e -> e.getSalary() > 4000);
        System.out.println(result2);
        //| noneMatch(Predicate p) | 检查是否没有匹配所有元素 |
        System.out.println("-------------------------------------noneMatch-------------------------------------");
        System.out.println("练习3:是否没有员工姓“雷”");
        boolean result3 = list.stream().noneMatch(e -> e.getName().startsWith("雷") );
        System.out.println(result3);

        //| findFirst()            | 返回第一个元素           |
        System.out.println("-------------------------------------findFirst-------------------------------------");
        System.out.println("练习4:按年龄从小到大排序并返回第一个结果(返回年龄最小的)");
        Optional<Employee> result4 = list.stream().sorted((e1, e2) -> Integer.compare(e1.getAge(), e2.getAge())).findFirst();
        System.out.println(result4);
        //| findAny()              | 返回当前流中的任意元素   |
        System.out.println("-------------------------------------findAny-------------------------------------");
        System.out.println("练习5:随机返回一个流中的结果");
        System.out.println("串行流:每次返回的结果都是list中的第一个数据");
        Optional<Employee> result5 = list.stream().findAny();
        System.out.println(result5);
        System.out.println("并行流:每次返回的结果都不同,但对同一个源的多次调用可能不会返回相同的结果。");
        Optional<Employee> result6 = list.parallelStream().findAny();
        System.out.println(result6);

        System.out.println("-------------------------------------count-------------------------------------");
        long count = list.stream().filter(employee -> employee.getSalary() > 5000).count();
        System.out.println("工资大于5000的人数:"+count);

        System.out.println("-------------------------------------max-------------------------------------");
        Optional<Employee> max = list.stream().max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
        System.out.println("工资最多的员工:"+max);

        System.out.println("-------------------------------------min-------------------------------------");
        Optional<Employee> min = list.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
        System.out.println("工资最少的员工:"+min);

        System.out.println("-------------------------------------forEach-------------------------------------");
        list.stream().forEach(System.out::println);
        
    }
}
2.归约
方法描述
reduce(T iden,BinaryOperator b)可以将流中元素反复结合起来,得到一个值。返回T
reduce(BinaryOperator b)可以将流中元素反复结合起来,得到一个值。返回Optional
@Test
public void test2(){
    /**
     * 归约
     * */

    //| reduce(T iden,BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。返回T           |
    //练习1:计算1-10的自然数的和
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    Integer re = list.stream().reduce(0, Integer::sum);//identity:初始值
    System.out.println(re);
    //| reduce(BinaryOperator b)        | 可以将流中元素反复结合起来,得到一个值。返回Optional<T> |
    //练习2:计算所有员工工资总和
    List<Employee> employeeList = EmployeeData.getEmployees();
    Optional<Double> reduce = employeeList.stream().map(e -> e.getSalary()).reduce(Double::sum);
    System.out.println(reduce);
    Optional<Double> reduce1 = employeeList.stream().map(e -> e.getSalary()).reduce((s1,s2)->s1+s2);
    System.out.println(reduce1);
}
3.收集
方法描述
collect(Collector c)将流转换为其他形式。接收以一个Collector接口的实现,用于给Stream中元素做汇总的方法

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

方法描述
toList把流中的元素收集到List
toSet把流中的元素收集到Set
toCollection把流中的元素收集到自定义的集合中
joining连接流中的每个字符串
counting计算流中元素个数
averagingDouble计算流中Double属性元素的平均值
summingDouble计算流中Double属性元素的和
summarizingDouble收集流中Double属性的统计值
groupingBy根据属性的值对流进行分组
partitioningBy根据true或false对数据进行分区
将流转换为其他形式
@Test
public void test3(){
    /**
     * 收集
     * */
    //collect(Collector c)将流转换为其他形式。接收以一个Collector接口的实现,用于给Stream中元素做汇总的方法
    //练习1:查找工资大于6000的员工,结果返回为一个List或Set
    List<Employee> employeeList = EmployeeData.getEmployees();
    System.out.println("-------------------------------------toList-------------------------------------");

    List<Employee> collect = employeeList.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
    collect.forEach(System.out::println);
    System.out.println("-------------------------------------toSet-------------------------------------");
    Set<Employee> collect1 = employeeList.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toSet());
    collect1.forEach(System.out::println);

    System.out.println("-------------------------------------toCollection自定义实现-------------------------------------");
    System.out.println("LinkedList:");
    LinkedList<Employee> collect2 = employeeList.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toCollection(LinkedList::new));
    collect2.forEach(System.out::println);
    System.out.println("Vector:");
    Collection<Employee> collect3 = employeeList.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toCollection(Vector::new));
    collect3.forEach(System.out::println);

}
字符串连接
@Test
public void test4(){
    System.out.println("-------------------------------------joining-------------------------------------");
    List<String> list = Arrays.asList("AAA","BBB","CCC","ddd","eee","fff","GGG","hhh");
    System.out.println("不指定连接符");
    String collect4 = list.stream().collect(Collectors.joining(""));
    System.out.println(collect4);
    System.out.println("指定连接符");
    String collect5 = list.stream().collect(Collectors.joining("---"));
    System.out.println(collect5);
    System.out.println("指定连接符以及前后缀");
    String collect6 = list.stream().collect(Collectors.joining("---","前缀","后缀"));
    System.out.println(collect6);
}
统计
@Test
public void test5(){
    List<Employee> list = EmployeeData.getEmployees();
    System.out.println("-------------------------------------count-------------------------------------");
    Long count = list.stream().collect(Collectors.counting());
    System.out.println(count);
    System.out.println("-------------------------------------average(以averagingDouble为例)-------------------------------------");
    Double averageSalary = list.stream().collect(Collectors.averagingDouble(Employee::getSalary));
    System.out.println("平均工资:"+averageSalary);
    System.out.println("-------------------------------------summing(以summingDouble为例)-------------------------------------");
    Double totalSalary = list.stream().collect(Collectors.summingDouble(Employee::getSalary));
    System.out.println("总工资:"+totalSalary);
    System.out.println("-------------------------------------summarizing(以summarizingDouble为例)-------------------------------------");
    DoubleSummaryStatistics summarizingResult = list.stream().collect(Collectors.summarizingDouble(Employee::getSalary));
    System.out.println("统计结果:"+summarizingResult);
    System.out.println("-------------------------------------groupingBy-------------------------------------");
    Map<Integer, List<Employee>> groupByResult = list.stream().collect(Collectors.groupingBy(Employee::getAge));
    System.out.println("分组结果:");
    for (Integer s : groupByResult.keySet()) {
        System.out.println("key:" + s);
        System.out.println("values:" + groupByResult.get(s));
    }
    System.out.println("-------------------------------------partitioningBy-------------------------------------");
    Map<Boolean, List<Employee>> partitioningByResult = list.stream().collect(Collectors.partitioningBy(e -> e.getAge() > 35));
    System.out.println("分区结果:");
    for (Boolean s : partitioningByResult.keySet()) {
        System.out.println("key:" + s);
        System.out.println("values:" + partitioningByResult.get(s));
    }
}
Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐