欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JAVA8新特性之Stream

程序员文章站 2022-06-15 17:24:29
...

一、简介

JAVA8新特性之Stream

二、Stream 

Stream 操作三步:

  • 创建 Stream
    一个数据源(如:集合、数组),获取一个流
  • 中间操作
    一个中间操作链,对数据源的数据进行处理
  • 终止操作(终端操作)
    一个终止操作,执行中间操作链,并产生结果

JAVA8新特性之Stream

1. 流的创建 

public class TestStreamAPI1{

    //创建 Stream
    @Test
    public void test1(){
        //1.可以通过 Collection 系列集合提供的 stream() 或 paralleStream()
        List<String> list = new ArrayList<>();
        Stream<String> stream1 = list.stream();

        //2.通过 Arrays 中的静态方法 stream() 获取数组流
        Employe[] emps = new Employe[10];
        Stream<Employe> stream2 = Arrays.stream(emps);

        //3.通过 Stream 类中的静态方法 of()
        Stream<String> stream3 = Stream.of("aa", "bb", "cc");

        //4.创建无限流
        //迭代
        Stream<Integer> stream4 = Stream.iterate(0, x -> x + 2);
        stream4.limit(10).forEach(System.out::println);

        //生成
        Stream<Double> stream5 = Stream.generate(() -> Math.random());
        stream5.limit(10).forEach(System.out::println);

    }
}

2. 流的中间操作

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

(1)筛选与切片

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

public class TestStreamAPI2 {

    //中间操作
    //内部迭代由 StreamAPI 完成
    /**
     * 筛选与切片
     * filter —— 接收 Lambda,从流中排除某些元素;
     * limit —— 截断流,使其元素不超过给定数量;
     * skip(n) —— 跳过元素,返回一个仍掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补;
     * distinct —— 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素。
     */

    List<Employe> employes = Arrays.asList(
            new Employe("张三", 18,9999.99),
            new Employe("李四", 38,5555.99),
            new Employe("王五", 50,6666.66),
            new Employe("赵六", 16,3333.33),
            new Employe("田七", 10,7777.77),
            new Employe("田七", 10,7777.77),
            new Employe("田七", 10,7777.77)
    );

    //filter 过滤
    @Test
    public void test1(){
        employes.stream()//创建流
                .filter(e->e.getAge() >= 35)//中间操作
                .forEach(System.out::println);//终止操作
    }

    //limit 限制
    @Test
    public void test2(){
        employes.stream()
                .filter(e->e.getSalary() >= 5000)
                .limit(2)
                .forEach(System.out::println);
    }

    //skip(n) 跳过
    @Test
    public void test3(){
        employes.stream()
                .filter(e -> e.getSalary() >= 5000)
                .skip(2)
                .forEach(System.out::println);
    }

    //distinct 去重,通过hashCode() 和 equals() 去重
    @Test
    public void test4(){
        employes.stream()
                .distinct()
                .forEach(System.out::println);
    }
}

【注意】distinct 去重的时候,Employee.java 中要覆写 equals() 和 hashCode() 方法:

public class Employe {

    private String name;
    private int age;
    private double salary;

    public Employe() {
    }

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

    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;
        Employe employe = (Employe) o;
        return age == employe.age &&
                Double.compare(employe.salary, salary) == 0 &&
                Objects.equals(name, employe.name);
    }

    @Override
    public int hashCode() {

        return Objects.hash(name, age, salary);
    }

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

(2)映射

         map —— 接收 Lambda,将元素转换成其它形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素;
   flatMap —— 接收一个函数作为参数,将流中的每个值都转换成另一个流,然后把所有流连接成一个流;

public class TestStreamAPI3 {

    List<Employe> employes = Arrays.asList(
            new Employe("张三", 18,9999.99),
            new Employe("李四", 38,5555.99),
            new Employe("王五", 50,6666.66),
            new Employe("赵六", 16,3333.33),
            new Employe("田七", 10,7777.77)
    );

    /**
     * 映射:
     *   map —— 接收 Lambda,将元素转换成其它形式或提取信息。接收一个函数作为参数,
     *          该函数会被应用到每个元素上,并将其映射成一个新的元素;
     *   flatMap —— 接收一个函数作为参数,将流中的每个值都转换成另一个流,然后把所有流连接成一个流;
     */

    @Test
    public void test1(){
        List<String> list = Arrays.asList("aaa","bbb","ccc","ddd","eee");

        list.stream()
                .map(String::toUpperCase)
                .forEach(System.out::println);

        System.out.println("--------------------------------");
        employes.stream()
                .map(Employe::getName)
                .forEach(System.out::println);

        System.out.println("--------------------------------");
        list.stream()
                .flatMap(TestStreamAPI3::filterCharacter)
                .forEach(System.out::print);

    }

    public static Stream<Character> filterCharacter(String str){
        ArrayList<Character> list = new ArrayList<>();

        for (Character ch : str.toCharArray()){
            list.add(ch);
        }
        return list.stream();
    }
}

(3)排序

        sorted() —— 自然排序(Comparable);
   sorted(Comparator com) —— 定制排序(Comparator);

public class TestStreamAPI4 {

    List<Employe> employes = Arrays.asList(
            new Employe("张三", 18,9999.99),
            new Employe("李四", 38,5555.99),
            new Employe("王五", 50,6666.66),
            new Employe("赵六", 16,3333.33),
            new Employe("田七", 10,7777.77),
            new Employe("田七", 16,7777.77),
            new Employe("田八", 10,7777.77)
    );

    /**
     * 排序
     *   sorted() —— 自然排序(Comparable)
     *   sorted(Comparator com) —— 定制排序(Comparator)
     */

    @Test
    public void test1(){
        List<String> list = Arrays.asList("ccc","eee","aaa","ddd","bbb");

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

        System.out.println("--------------------------------------------------");
        employes.stream()
                .sorted((e1, e2) -> {
                    if (e1.getAge() == e2.getAge()){
                        return e1.getName().compareTo(e2.getName());
                    }else {
                        return e1.getAge()-e2.getAge();
                    }
                })
                .forEach(System.out::println);
    }
}

3. 终止操作

(1)查找与匹配

/**
 * 终止操作
 */
public class TestStreamAPI5 {

    List<Employe> emps = Arrays.asList(
            new Employe("张三", 18,9999.99, Status.FREE),
            new Employe("李四", 38,5555.99, Status.BUSY),
            new Employe("王五", 50,6666.66, Status.VOCATION),
            new Employe("赵六", 16,3333.33, Status.FREE),
            new Employe("田七", 10,7777.77, Status.BUSY),
            new Employe("李八", 16,8888.88, Status.VOCATION)
    );

    /**
     * 查找与匹配
     *  (1)allMatch —— 检查是否匹配所有元素;
     *  (2)anyMatch —— 检查是否至少匹配一个元素;
     *  (3)noneMatch —— 检查是否没有匹配所有元素;
     *  (4)findFirst —— 返回第一个元素;
     *  (5)findAny —— 返回当前流中的任意元素;
     *  (6)count —— 返回流中元素的总数;
     *  (7)max —— 返回流中最大值;
     *  (8)min —— 返回流中最小值;
     */

    @Test
    public void test1(){
        //是否匹配所有元素
        boolean b1 = emps.stream()
                .allMatch(e -> e.getStatus().equals(Status.FREE));
        System.out.println(b1);

        //至少匹配一个元素
        boolean b2 = emps.stream()
                .anyMatch(e -> e.getStatus().equals(Status.FREE));
        System.out.println(b2);

        //没有匹配的元素
        boolean b3 = emps.stream()
                .noneMatch(e -> e.getStatus().equals(Status.FREE));
        System.out.println(b3);

        //先按工资排序,然后再找出第一个
        Optional<Employe> op1 = emps.stream()
//                .sorted((o1, o2) -> Double.compare(o1.getSalary(), o2.getSalary()))
                .sorted(Comparator.comparingDouble(Employe::getSalary))
                .findFirst();

        System.out.println(op1.get());

        //返回当前流中的任意元素
        Optional<Employe> op2 = emps.parallelStream()
                .filter(e -> e.getStatus().equals(Status.FREE))
                .findAny();
        System.out.println(op2.get());

    }

    @Test
    public void test2(){
        //总数
        long count = emps.stream()
                .count();
        System.out.println(count);

        //工资最高
        Optional<Employe> max = emps.stream()
//                .max((x, y) -> Double.compare(x.getSalary(), y.getSalary()));
                .max(Comparator.comparingDouble(Employe::getSalary));
        System.out.println(max.get());

        //年龄最小
        Optional<Employe> min = emps.stream()
//                .min((x, y) -> Double.compare(x.getAge(), y.getAge()));
                    .min(Comparator.comparingDouble(Employe::getAge));
        System.out.println(min.get());

        //最低工资
        Optional<Double> minSalary = emps.stream()
                .map(Employe::getSalary)
                .min(Double::compareTo);
        System.out.println(minSalary.get());

    }
}

(2)规约与收集

归约:
   reduce(T identity, BinaryOperator)/reduce(BinaryOperator) —— 可以将流中元素反复结合起来,得到一个值。

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

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

/**
 * 终止操作
 */
public class TestStreamAPI6 {

    List<Employe> emps = Arrays.asList(
            new Employe("张三", 18,9999.99, Status.FREE),
            new Employe("李四", 38,5555.99, Status.BUSY),
            new Employe("王五", 50,6666.66, Status.VOCATION),
            new Employe("赵六", 16,3333.33, Status.FREE),
            new Employe("田七", 10,7777.77, Status.BUSY),
            new Employe("田七", 16,8888.88, Status.VOCATION)
    );

    /**
     * 归约
     *  reduce(T identity, BinaryOperator)/reduce(BinaryOperator) —— 可以将流中元素反复结合起来,
     *                                                               得到一个值。
     */

    @Test
    public void test1(){
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        Integer sum = list.stream()
                .reduce(0, (x, y) -> x + y);
        System.out.println(sum);

        System.out.println("-------------------------------------------");

        //计算工资总和
        Optional<Double> op = emps.stream()
                .map(Employe::getSalary)
                .reduce(Double::sum);
        System.out.println(op.get());
    }

    /**
     * 收集:
     *    collect —— 将流转换为其他形式。接收一个Collector 接口的实现,
     *                用于给 Stream 中元素做汇总的方法。
     */
    @Test
    public void test2(){
        List<String> list = emps.stream()
                .map(Employe::getName)
                .collect(Collectors.toList());
        list.forEach(x->System.out.print(x+"\t\t"));

        System.out.println("\n ----------------------------------------------");
        Set<String> set = emps.stream()
                .map(Employe::getName)
                .collect(Collectors.toSet());
        set.forEach(x-> System.out.print(x+"\t\t"));
    }

    @Test
    public void test3(){
        //人员总数
        Long count = emps.stream().count();
//                .collect(Collectors.counting());
        System.out.println("总数量:"+count);

        //工资平均值
        Double avgSalary = emps.stream()
                .collect(Collectors.averagingDouble(Employe::getSalary));
        System.out.println("平均工资:"+avgSalary);

        //工资总和
        Double sumSalary = emps.stream().mapToDouble(Employe::getSalary).sum();
//                .collect(Collectors.summingDouble(Employe::getSalary));
        System.out.println("工资总和:"+sumSalary);

        //工资最大值
        Optional<Employe> maxSalary = emps.stream()
                .max(Comparator.comparingDouble(Employe::getSalary));
//                .max((x, y) -> Double.compare(x.getSalary(), y.getSalary()));
//                .collect(Collectors.maxBy((x, y) -> Double.compare(x.getSalary(), y.getSalary())));
        System.out.println("工资最大值:"+ maxSalary.get().getSalary());

        //工资最小值
        Optional<Employe> minSalary = emps.stream()
                .min(Comparator.comparingDouble(Employe::getSalary));
//                .min((x, y) -> Double.compare(x.getSalary(), y.getSalary()));
//                .collect(Collectors.minBy((x, y) -> Double.compare(x.getSalary(), y.getSalary())));
        System.out.println("工资最小值:"+ minSalary.get().getSalary());
    }
}

拓展:并行流、串行流

1. Fork/join框架

JAVA8新特性之Stream

JAVA8新特性之Stream

2. 并行流Demo

计算和

JAVA8新特性之Stream

【提示】底层是基于Fork/join思想的!

拓展:Optional

JAVA8新特性之Stream