Java8 新特性之Stream

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

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

 注意:

        1、Stream 自己不会存储元素;

        2、Stream 不会改变源对象。相反,他们会返回一个持有结果的新 Stream;

        3、Stream 操作是延迟执行的。意味着他们会等到需要结果的时候才执行。

Stream 操作的三个步骤

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

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

        终止操作(终端操作):一个终止操作,执行中间操作链,并产生结果

1、创建 Stream

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


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

    // 3 通过 Stream 类中的静态方法 of()
    Stream<String> stream3 = Stream.of("aaa", "bbb", "ccc", "ddd", "eee");


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

    // 5、 generate() 生成
    Stream.generate(() -> Math.random()).limit(5).forEach(System.out::println);
}

Stream 的中间操作

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

筛选与切片

方法描述
filter(Predicate p) 接收 Lambda 从流中排除某些元素
distinct()  

筛选,通过流所生成元素的 hashCode()  equals() 去除重复元素,

 如果要采用 distinct() 去重需要重写类的 hashCode() 和 equals() 方法 

limit(long maxSize)截断流,使其元素不超过给定数量
skip(long n)

跳过元素,返回一个扔掉前 n 个元素的流。若流中元素不足 n 个, 

则返回一个空流,与 limit(n) 互补

Employee 的定义

import java.util.HashMap;
import java.util.Objects;

public class Employee {
    private String name;
    private Integer age;
    private Double salary;

    public Employee() {}

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

    public Employee(String name, Integer 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 Integer getAge() {
        return age;
    }

    public void setAge(Integer 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 Objects.equals(name, employee.name) && Objects.equals(age, employee.age) && Objects.equals(salary, employee.salary);
    }


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

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

}
    List<Employee> employees = Arrays.asList(
            new Employee("张三", 28, 8000.0),
            new Employee("李四", 58, 28000.0),
            new Employee("王五", 27, 8000.0),
            new Employee("马六", 22, 7800.0),
            new Employee("田七", 38, 180000.0),
            new Employee("田七", 38, 180000.0),
            new Employee("田七", 38, 180000.0),
            new Employee("田七", 38, 180000.0)
    );


    // 内部迭代:迭代操作由 Stream API 实现
    @Test
    public void test2() {

        // 中间操作
        Stream<Employee> stream = employees.stream()
                .filter((e) -> {
                    System.out.println("中间操作执行~~~");
                    return e.getAge() > 35;
                });

        // 终止操作: 一次性执行全部内容,即”惰性求值“
        stream.forEach(System.out::println);
    }

    @Test
    public void test3(){
        employees.stream()
                .filter((e)->{
                    System.out.println("出现的行数~~~");
                    return e.getSalary() > 5000;
                })
                .limit(2)
                .forEach(System.out::println);
    }

    @Test
    public void test4(){
        employees.stream()
                .filter((e)-> e.getSalary() > 5000)
                .skip(2)
                .forEach(System.out::println);
    }


    @Test
    public void test5(){
        employees.stream()
                .filter((e)-> e.getSalary() > 5000)
                .skip(2)
                .distinct()
                .forEach(System.out::println);
    }

映射

方法描述
map(Function f)

接收一个函数作为参数,该函数会被应用到每个元素上,

并将其映射成一个新的元素

mapToDouble(ToDoubleFunction f)

接收一个函数作为参数,该函数会被应用到每个元素上,

并产生一个新的 DoubleStream

mapToInt(ToIntFunction f)     

接收一个函数作为参数,该函数会被应用到每个元素上,

并产生一个新的 IntStream

mapToLong(ToLongFunction f) 

接收一个函数作为参数,该函数会被应用到每个元素上,

产生一个新的元素 LongStream

floatMap(Function f) 

接收一个函数作为参数,将流中的每一个值都换成另一个流,

然后把所有流连接成一个流

   @Test
    public void test6() {
        /**
         * 映射
         * map(Function f)	接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
         */
        List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
        list.stream()
                .map((str) -> str.toUpperCase())
                .forEach(System.out::println);


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


        // 提取员工姓名
        employees.stream()
                .map(Employee::getName)
                .forEach(System.out::println);

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



        /*
            "aaa", "bbb", "ccc", "ddd"
            这里将 list 中的每一项都通过 filterCharacter, 每一项都返回一个 stream()
            list.stream().map() 操作的结果返回的也是一个 stream()
            所以这里返回的是 stream 的嵌套类型 {{a,a,a}, {b, b, b}, ...}
         */
        Stream<Stream<Character>> stream = list.stream().map(StreamTest::filterCharacter);
        stream.forEach((item) -> {
            item.forEach(System.out::println);
        });


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

        /*
         * flatMap 在 map 的基础上将所有的流合并成一个流, 结果就是 {a, a, a, b, b, b, ...}
         */
        Stream<Character> stream1 = list.stream().flatMap(StreamTest::filterCharacter);
        stream1.forEach(System.out::println);
    }


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

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

排序

方法描述
sorted()自然排序(Comparable
sorted(Comparator com) 定制排序 (Comparator)
 @Test
    public void test7() {

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

        List<String> list = Arrays.asList("bbb", "ddd", "ccc", "aaa");
        list.stream()
                .sorted()
                .forEach(System.out::println);


        
        // 年龄相同按照姓名排序,否则按照年龄排序
        employees.stream().sorted((e1, e2)->{
            if(e1.getAge().equals(e2.getAge())){
                return e1.getName().compareTo(e2.getName());
            }else {
                return e1.getAge().compareTo(e2.getAge());
            }
        }).forEach(System.out::println);
    }

Stream 的终止操作 

        终止操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如 List、Integer 甚至是 void

查找与匹配

方法描述
allMatch(Predicate p)   检查是否匹配所有元素
anyMatch(Predicate p)   检查是否至少匹配一个元素
noneMatch(Predicate p)   检查是否没有匹配所有元素
findFirst()  返回第一个元素
findAny()    返回当前流中的任意元素
count()返回流中元素的总个数

Optional<T> max(Comparator<? super T> comparator);

返回流中最大值

Optional<T> min(Comparator<? super T> comparator);

返回流中最小值

void forEach(Consumer<? super T> action);

内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反,Stream API 使用内部迭代——它帮我们完成迭代)

重新修改一下 Employee 类,添加一些额外的内容

import java.util.HashMap;
import java.util.Objects;

public class Employee {
    private String name;
    private Integer age;
    private Double salary;
    private Status status;

    public enum Status {FREE, BUSY, VOCATION}

    public Employee() {
    }

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

    public Employee(String name, Integer age, Double salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    public Employee(String name, Integer age, Double salary, Status status) {
        this.name = name;
        this.age = age;
        this.salary = salary;
        this.status = status;
    }

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Status status) {
        this.status = status;
    }

    public Status getStatus() {
        return status;
    }

    public void setAge(Integer 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 Objects.equals(name, employee.name) && Objects.equals(age, employee.age) && Objects.equals(salary, employee.salary);
    }


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

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", salary=" + salary +
                ", status=" + status +
                '}';
    }
}
  List<Employee> employees = Arrays.asList(
            new Employee("张三", 28, 8000.0, Employee.Status.BUSY),
            new Employee("李四", 58, 28000.0, Employee.Status.FREE),
            new Employee("王五", 27, 8000.0, Employee.Status.VOCATION),
            new Employee("马六", 22, 7800.0, Employee.Status.FREE),
            new Employee("田七", 38, 180000.0, Employee.Status.VOCATION),
            new Employee("田七", 38, 180000.0, Employee.Status.BUSY),
            new Employee("田七", 38, 180000.0, Employee.Status.VOCATION),
            new Employee("田七", 38, 180000.0, Employee.Status.BUSY)
    );
    @Test
    public void test8() {
        // allMatch 检查是否匹配所有元素
        boolean flag = employees.stream().allMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));
        System.out.println(flag);

        // anyMatch 检查是否匹配一个元素
        boolean flag2 = employees.stream().anyMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));
        System.out.println(flag2);

        // noneMatch 检查是否没有匹配所有元素
        boolean flag3 = employees.stream().noneMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));
        System.out.println(flag3);


        // 如果 findFirst() 的值可能为 null 的话,将结果封装到 Optional 容器中去
        // employees 可能就是空的,导致 findFirst 为 null
        Optional<Employee> opt = employees.stream()
                                          .sorted((e1, e2) -> -Double.compare(e1.getSalary(), e2.getSalary()))
                                          .findFirst();

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



        // 返回 employees 中任意一个 Status 是 FREE 的 Employee 对象
        Optional<Employee> optional = employees.stream()
                .filter(e -> e.getStatus().equals(Employee.Status.FREE))
                .findAny();
        System.out.println(optional.get());


        // 返回 employees 中的总数
        long count = employees.stream().count();


        // 获取 employees 中工资最小的员工信息
        Optional<Employee> max = employees.stream().max((e1, e2) -> e1.getSalary().compareTo(e2.getSalary()));
        System.out.println(max.get());


        // 获取 employees 中最小的工资
        Optional<Double> minSalary = employees.stream()
                .map(Employee::getSalary)
                .min(Double::compareTo);
        System.out.println(minSalary);
    }

规约

方法

描述

T reduce(T identity, BinaryOperator<T> accumulator);

可以将流中的元素反复结合起来,得到一个值,返回 T

Optional<T> reduce(BinaryOperator<T> accumulator);

可以将流中的元素反复结合起来,得到一个值,返回 Optional<T>

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

    @Test
    public void test9() {
        List<Integer> list = Arrays.asList(1, 2, 3, 3, 4);

        // T reduce(T identity, BinaryOperator<T> accumulator);
        // 将  0 赋值给 x, 将 list 中的 1 赋值给 y ==> 执行 x + y
        // 将 x+y 的结果赋值给 x, 将 将 list 中的 2 赋值给 y ==> 执行 x + y, ...
        Integer sum = list.stream().reduce(0, (x, y) -> x + y);
        System.out.println(sum);

        // 可以将流中的元素反复结合起来,得到一个值
        Optional<Double> reduce = employees.stream().map(Employee::getSalary).reduce(Double::sum);
        System.out.println(reduce.get());
    }

收集

方法

描述

<R, A> R collect(Collector<? super T, A, R> collector);

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

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

    // 收集
    // collection —— 将流转换为其他形式,接收一个 Collection 接口的实现,用于给 Stream 中元素做汇总的方法
    @Test
    public void test10() {
        System.out.println("---------收集员工的姓名 到 List 中------------");
        // 收集员工的姓名 到 List 中
        List<String> collect = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.toList());

        collect.forEach(System.out::println);

        System.out.println("---------收集员工的姓名 到 Set 中------------");

        // 收集员工的姓名 到 Set 中
        Set<String> set = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.toSet());
        set.forEach(System.out::println);

        System.out.println("----------收集员工的姓名 到 hashSet 中-----------");


        // 收集员工的姓名 到 hashSet 中
        HashSet<String> hashSet = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.toCollection(HashSet::new));
        hashSet.forEach(System.out::println);


        System.out.println("---------统计总数------------");
        // 统计总数
        Long nums = employees.stream()
                .collect(Collectors.counting());
        System.out.println(nums);

        System.out.println("---------统计工资的平均值------------");
        // 统计工资的平均值
        Double averageSalary = employees.stream().collect(Collectors.averagingDouble(Employee::getSalary));
        System.out.println(averageSalary);


        System.out.println("---------统计工资的总和------------");
        // 统计工资的平均值
        Double sumSalary = employees.stream()
                .collect(Collectors.summingDouble(Employee::getSalary));
        System.out.println(sumSalary);

        System.out.println("---------获取工资的最大的员工信息------------");
        // 统计工资的最大值
        Optional<Employee> max = employees.stream()
                .collect(Collectors.maxBy((e1, e2) -> e1.getSalary().compareTo(e2.getSalary())));
        System.out.println(max.get());


        System.out.println("---------统计工资的最小值------------");
        // 统计工资的最大值
        Optional<Double> min = employees.stream()
                .map(Employee::getSalary)
                .collect(Collectors.minBy(Double::compare));

        System.out.println(min.get());
    }


    // 分组
    @Test
    public void test11() {
        // 按照 Status 分组
        Map<Employee.Status, List<Employee>> map = employees.stream().collect(Collectors.groupingBy(Employee::getStatus));
        System.out.println(map);

        // 多级分组
        // 先按照状态分组,再按照年龄段分组
        Map<Employee.Status, Map<String, List<Employee>>> collect = employees.stream().collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {
            if (((Employee) e).getAge() <= 35) {
                return "青年";
            } else if (((Employee) e).getAge() <= 50) {
                return "中年";
            } else {
                return "老年";
            }
        })));

        System.out.println(collect);
    }

    // 分区
    @Test
    public void test12() {
        // 将 薪酬超过 1W的 员工分开
        Map<Boolean, List<Employee>> collect = employees.stream()
                .collect(Collectors.partitioningBy((e) -> e.getSalary() > 10000));
        System.out.println(collect);
    }

    @Test
    public void test13() {
        // 获取 Employee 中的全部薪酬
        DoubleSummaryStatistics collect = employees.stream()
                .collect(Collectors.summarizingDouble(Employee::getSalary));

        // 获取薪酬的五种指标
        System.out.println(collect.getMax());
        System.out.println(collect.getAverage());
        System.out.println(collect.getCount());
        System.out.println(collect.getMin());
        System.out.println(collect.getSum());
    }


    @Test
    public void test14() {
        // joining 中第一个参数是分隔字符符号, 第二个参数是 前缀, 第三个参数是 后缀
        // 获取 Employee 中的全部薪酬
        String collect = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.joining(" , ", "{", "}"));
        System.out.println(collect);
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值