java8 -- Stream

泄露秘密 提交于 2020-02-26 23:21:09

Stream API (java.util.stream.*)

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

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

1. 创建 Stream,可创建有限流和无限流

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

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

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

    // 4. 创建无限流
    // 4.1 迭代,使用 iterate()
        // 查看 iterate 方法源码可得,第一个参数是一个种子,也就是初始值,第二个参数是一个 UnaryOperator 函数式接口,
        // 继承自 Function 接口,只是限定了 Function 接口的参数值和返回值是相同类型
    Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 2);
    stream4.limit(4)
            .forEach(System.out::println);

    // 4.2 生成,generate 的参数是 Supplier 函数式接口
    Stream.generate(() -> (int)(Math.random() * 1000))
            .limit(5)
            .forEach(System.out::println);
}

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

2. 中间操作

/**
 * 筛选与切片
 * filter -- 接收 Lambda,从流中排除某些元素
 * limit -- 截断流,使其元素不超过给定数量
 * skip(n) -- 跳过元素,返回一个去除前 n 个元素的流,若流中元素不足 n 个,则返回一个空流,与 limut(n) 互补
 * distinct -- 筛选,通过流所生成元素的 hashCode() 与 equals() 取出重复元素
 */

// filter -- 接收 Lambda,从流中排除某些元素
// 内部迭代:迭代操纵由 Stream API 完成
@Test
public void test1() {
    // 中间操作:不会执行任何操作
    Stream<Employee> stream = employees.stream()
                                        .filter((e) -> {
                                            System.out.println("Stream API 的中间操作");
                                            return e.getAge() > 35;
                                        });
    // 终止操作:一次性执行全部内容,即 “惰性求值”
    stream.forEach(System.out::println);
}

// 外部迭代
@Test
public void test2 () {
    Iterator<Employee> iterator = employees.iterator();
    while (iterator.hasNext()) {
        System.out.println(iterator.next());
    }
}

// limit -- 截断流,使其元素不超过给定数量
@Test
public void test3 () {
    employees.stream()
            .filter((e) -> {
                System.out.println("短路");
                return e.getSalary() < 5000;
            })
            .limit(2)
            .forEach(System.out::println);
}

// skip(n) -- 跳过元素,返回一个去除前 n 个元素的流,若流中元素不足 n 个,则返回一个空流,与 limut(n) 互补
@Test
public void test4() {
    employees.stream()
            .filter((e) -> e.getSalary() < 5000)
            .skip(1)
            .forEach(System.out::println);
}

// distinct -- 筛选,通过流所生成元素的 hashCode() 与 equals() 取出重复元素
@Test
public void test5() {
    // 需要重写  Employee 的 hashCode() 和 equals()
    employees.stream()
            .filter((e) -> e.getSalary() < 5000)
            .skip(2)
            .distinct()
            .forEach(System.out::println);
}
/**
 * 映射
 * map -- 接收 Lambda,将元素装换成其他形式或提取信息。接受一个函数作为参数,
 *         该函数会被应用到每个元素上,并将其映射成一个新的元素
 * flatMap -- 接收一个函数作为参数,将流中的每个值都转换成另一个流,然后把所有流连成一个
 */

@Test
public void test6 () {
    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((e) -> e.getName())
            .map(Employee::getName)
            .forEach(System.out::println);

    // 理解
    Function<Employee, String> fun1 = (emp) -> emp.getName();
    Function<Employee, String> fun2 = Employee::getName;
    Supplier<String> supplier2 = Employee::staticMethod;

    Employee employee = new Employee();
    Supplier<String> supplier1 = employee::getName;

    // Lambda 尖括号右面的内容实际上是函数式接口方法的方法体和尖括号里面调用的方法不相关
    // 方法调用 :: 的一条规则就是 :: 右面的方法也就是调用的方法要和函数式接口中的方法入参返回值一致

    // Function<Employee, String> fun2 = Employee::getName; 是一种特殊的写法,Function 接口中的 R apply(T t)
    // 方法体中的语句只有一句且是对 T 的一个方法的引用。上面的也就是我想要调用 Employee 对象的 getName() 方法。
}

// 扁平化处理之前
@Test
public void test7_1() {
    List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
    Stream<Stream<Character>> stream = list.stream()
            .map(StreamAPI2::filterCharacter);
    stream.forEach((sm) -> {
        sm.forEach(System.out::println);
    });
}

// 扁平化处理,将所有的流整合成一个流
@Test
public void test7_2() {
    List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
    Stream<Character> stream = list.stream()
            .flatMap(StreamAPI2::filterCharacter);
    stream.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(Compartor com) -- 定制排序
 */
@Test
public void test8() {
    List<String> list = Arrays.asList("b", "cc", "aaa", "bbb");
    list.stream()
            .sorted()
            .forEach(System.out::println);

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

    Comparator<String> comparator = (x, y) -> {
        if (x.length() > y.length()) {
            return 1;
        } else if (x.length() == y.length()) {
            return x.compareTo(y);
        } else {
            return -1;
        }
    };

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

    employees.stream()
            .sorted((x, y) -> {
                if (x.getAge().equals(y.getAge())) {
                    return x.getName().compareTo(y.getName());
                } else {
                    return x.getAge().compareTo(y.getAge());
                }
            }).forEach(System.out::println);
}

3.终止操作

// 3.终止操作
/**
 * 查找与匹配
 * allMatch -- 检查是否匹配所有元素
 * anyMatch -- 检查是狗至少匹配一个元素
 * noneMatch -- 检查是否没有匹配所有元素
 *
 * findFirst -- 返回第一个元素
 * findAny -- 返回当前流中的任意元素
 * count -- 返回流中数据总个数
 * max -- 返回流中的最大值
 * min -- 返回流中的最小值
 */

@Test
public void test9() {
    List<Employee> employeeList = Arrays.asList(
            new Employee("张三", 18, 99999.99, Employee.Status.FREE),
            new Employee("李四", 50, 2222.22, Employee.Status.BUSY),
            new Employee("王五", 16, 666.66, Employee.Status.VOCATION),
            new Employee("赵六", 15, 3333.33, Employee.Status.FREE),
            new Employee("赵六", 15, 3333.33, Employee.Status.BUSY));

    boolean result = employeeList.stream()
            .allMatch((emp) -> emp.getStatus().equals(Employee.Status.BUSY));
    System.out.println(result);

    result = employeeList.stream()
            .anyMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));
    System.out.println(result);

    result = employeeList.stream()
            .noneMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));
    System.out.println(result);

    // Optional 用来防止空指针异常
    Optional<Employee> op = employeeList.stream()
            .sorted((x, y) -> {
                if (x.getAge().equals(y.getAge())) {
                    return x.getName().compareTo(y.getName());
                } else {
                    return x.getAge().compareTo(y.getAge());
                }
            }).findFirst();
    System.out.println(op.get());

    // 从空闲的人员中任意挑选一个工作
    Optional<Employee> op2 = employeeList.stream()
            .filter((e) -> e.getStatus().equals(Employee.Status.FREE))
            .findAny();
    System.out.println(op2.get());

    System.out.println("--------------------------------------------");
    Optional<Employee> op3 = employeeList.stream()
            .max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
    System.out.println(op3.get());

    System.out.println("--------------------------------------------");
    Optional<Double> op4 = employeeList.stream()
            .map(Employee::getSalary)   // Function<T, R> 简单写法
            .min(Double::compare);      // a.method(b) 的简单写法
    System.out.println(op4.get());
}
/**
 * 规约
 * reduce(T identity, BinaryOperator) / reduce(BinaryOperator) -- 可以将流中元素反复结合起来,得到一个值
 * map-reduce 模式,Google 使用它来进行网络搜索而出名,提取和操作
 */
@Test
public void test10 () {
    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);
    // 上面的表达式的以上如下所示
//        int x = 0, y, sum;  // 指定的 0 为 x 的初始值
//        for (Integer num : list) {
//            y = num;
//            x = x + y;
//        }
//        sum = x;
    System.out.println(sum);
}

@Test
public void test11 () {
    List<Employee> employeeList = Arrays.asList(
            new Employee("张三", 18, 99999.99, Employee.Status.FREE),
            new Employee("李四", 50, 2222.22, Employee.Status.BUSY),
            new Employee("王五", 16, 666.66, Employee.Status.VOCATION),
            new Employee("赵六", 15, 3333.33, Employee.Status.FREE),
            new Employee("赵六", 15, 3333.33, Employee.Status.BUSY));
    // 使用 reduce 一个参数的时候是可能为空的,两个参数因为指定了初始值所以不会为空
    // 可能为空的使用 Optional
    Optional<Double> sum = employeeList.stream()
                .map(Employee::getSalary)
                .reduce(Double::sum);
    System.out.println(sum.get());
}

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

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

@Test
public void test12 () {
    List<Employee> employeeList = Arrays.asList(
            new Employee("张三", 18, 99999.99, Employee.Status.FREE),
            new Employee("李四", 50, 2222.22, Employee.Status.BUSY),
            new Employee("王五", 16, 666.66, Employee.Status.VOCATION),
            new Employee("赵六", 15, 3333.33, Employee.Status.FREE),
            new Employee("赵六", 15, 3333.33, Employee.Status.BUSY));

    List<String> list = employeeList.stream()
            .map(Employee::getName)
            .collect(Collectors.toList());
    System.out.println(list);

    System.out.println("----------------------------------");
    Set<String> set = employeeList.stream()
            .map(Employee::getName)
            .collect(Collectors.toSet());
    System.out.println(set);

    System.out.println("----------------------------------");
    // 添加到特殊的集合对象中,比如 HashSet 可以先将收集到的数据转换成 Collection 中
    // 然后指定这种 Collection 为 HashSet
    HashSet<String> hashSet = employeeList.stream()
            .map(Employee::getName)
            .collect(Collectors.toCollection(HashSet::new));
    hashSet.forEach(System.out::println);
}
@Test
public void test13 () {
    // 收集器中的计算
    List<Employee> employeeList = Arrays.asList(
            new Employee("张三", 18, 99999.99, Employee.Status.FREE),
            new Employee("李四", 50, 2222.22, Employee.Status.BUSY),
            new Employee("王五", 16, 666.66, Employee.Status.VOCATION),
            new Employee("赵六", 15, 3333.33, Employee.Status.FREE),
            new Employee("赵", 15, 3333.33, Employee.Status.BUSY));
    // 总数,计算有多少员工
    Long sum = employeeList.stream()
            .collect(Collectors.counting());
    System.out.println(sum);

    // 工资总数
    Double sumSalary = employeeList.stream()
            .collect(Collectors.summingDouble(Employee::getSalary));
    System.out.println(sumSalary);

    double sum2 = employeeList.stream()
            .map((e) -> e.getSalary())
            .reduce(0D, (x, y) -> x + y);
    System.out.println(sum2);

    // 平均值
    Double average = employeeList.stream()
            .collect(Collectors.averagingDouble(Employee::getSalary));
    System.out.println(average);

    System.out.println("-------------------------------------------------");
    // 最大值,工资最大的员工
    Optional<Employee> employee = employeeList.stream()
            .collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
    // 最大的工资
    Optional<Double> maxSalary = employeeList.stream()
            .map(Employee::getSalary)
            .max(Double::compare); // 静态方法的方法引用
    System.out.println(employee.get());
    System.out.println(maxSalary.get());

    double max = employeeList.stream()
            .map(Employee::getSalary)
            .reduce(0D, (x, y) -> {
                if (Double.compare(x, y) < 0) {
                    return y;
                } else {
                    return x;
                }
            });
    System.out.println("maxSalary: " + max);

    System.out.println("-------------------------------------------------");
    // 获取年龄最小的员工
    Optional<Employee> minAgeEmployee = employeeList.stream()
            .collect(Collectors.minBy((x, y) -> Double.compare(x.getAge(), y.getAge())));
    System.out.println(minAgeEmployee.get());
@Test
public void test14 () {
    // 收集器中的分组
    List<Employee> employeeList = Arrays.asList(
            new Employee("张三", 18, 99999.99, Employee.Status.FREE),
            new Employee("李四", 50, 2222.22, Employee.Status.BUSY),
            new Employee("王五", 16, 666.66, Employee.Status.VOCATION),
            new Employee("赵六", 15, 3333.33, Employee.Status.FREE),
            new Employee("赵", 15, 3333.33, Employee.Status.BUSY));
    // 安装状态进行分组
    Map<Employee.Status, List<Employee>> groups = employeeList.stream()
            .collect(Collectors.groupingBy(Employee::getStatus));
    groups.forEach((key, value) -> {
        System.out.println(key);
        System.out.println(value);
    });

    System.out.println("--------------------------------");
    for (Map.Entry<Employee.Status, List<Employee>> map : groups.entrySet()) {
        System.out.println(map.getKey());
        System.out.println(map.getValue());
    }

    System.out.println("----------------------------------------------------");
    // 多级分组,传递的数据第二个参数还可以是 Collector
   Map<Double, Map<String, List<Employee>>> map2 =  employeeList.stream()
            .collect(Collectors.groupingBy(Employee::getSalary,
                    Collectors.groupingBy((e) -> {
                        if (((Employee)e).getAge() <= 35){
                            return "青年";
                        } else if (((Employee)e).getAge() <= 50) {
                            return "中年";
                        } else {
                            return "老年";
                        }
                    })));
    map2.forEach((key1, value1) -> {
        System.out.println(key1);
        value1.forEach((key2, value2) -> {
            System.out.println(key2);
            System.out.println(value2);
        });
    });
}
@Test
public void test15 () {
    // 分区,符合条件的为一个区域,不符合的为另一个区域
    List<Employee> employeeList = Arrays.asList(
            new Employee("张三", 18, 99999.99, Employee.Status.FREE),
            new Employee("李四", 50, 2222.22, Employee.Status.BUSY),
            new Employee("王五", 16, 666.66, Employee.Status.VOCATION),
            new Employee("赵六", 15, 3333.33, Employee.Status.FREE),
            new Employee("赵", 15, 3333.33, Employee.Status.BUSY));

    Map<Boolean, List<Employee>> map = employeeList.stream()
            .collect(Collectors.partitioningBy((e) -> e.getSalary() > 8000));
    map.forEach((key, value) -> {
        System.out.println(key);
        System.out.println(value);
    });
}

Collectors.joining(",", “%%%”, “%%”)

@Test
public void test16 () {
    // 其他的收集方式
    List<Employee> employeeList = Arrays.asList(
            new Employee("张三", 18, 99999.99, Employee.Status.FREE),
            new Employee("李四", 50, 2222.22, Employee.Status.BUSY),
            new Employee("王五", 16, 666.66, Employee.Status.VOCATION),
            new Employee("赵六", 15, 3333.33, Employee.Status.FREE),
            new Employee("赵", 15, 3333.33, Employee.Status.BUSY));
    // 将各种计算值整合
    DoubleSummaryStatistics dss = employeeList.stream()
            .collect(Collectors.summarizingDouble(Employee::getSalary));
    System.out.println(dss.getMax());
    System.out.println(dss.getAverage());
    System.out.println(dss.getCount());

    System.out.println("--------------------------------");
    // 连接字符串,指定连接值,开始值,结尾值
    String str = employeeList.stream()
            .map(Employee::getName)
            .collect(Collectors.joining(",", "===", "==="));
            // joining 不传递参数将直接拼接
    System.out.println(str);
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!