Java 8 新特性 Ⅲ

EmployeeData.java (下面需要用到的数据)

java 复制代码
public class EmployeeData {
   public static List<Employee> getEmployees(){ // 返回list集合
       List<Employee> list = new ArrayList<>(); // 创建数组
       list.add(new Employee(19, 1000.0,1,"sam"));
       list.add(new Employee(18, 2000.0,2,"Tom"));
       list.add(new Employee(29, 54000.0,3,"Tonny"));
       list.add(new Employee(20, 100032.0,4,"sammy"));
       list.add(new Employee(39, 12100.0,5,"Sunny"));
​
       return list;
   }
}

Steam API

  • Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。极大的提高效率

  • Stream 是Java8中处理集合的关键抽象概念, 它可以指定系统为对集合进行的操作, 可以执行非常复杂的查找、过滤和映射数据的操作

    使用 Stream API对集合数据进行操作, 就类似于使用SQL执行的数据库查询 (Stream API 可以处理如redis等非关系型数据库的数据)

什么是 Stream

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

Stream与Collection集合的区别: Collection 是一种静态的内存数据结构, 讲的是数据 (面向内存, 存储在内存中); 而Stream是有关计算的, 讲的是计算, 面向CPU, 通过CPU实现计算。

简而言之, Stream API

注:

  • Stream 自身不会存储数据
  • Stream 不会改变源对象, 会返回一个持有结果的新Stream
  • Stream 操作是延迟执行的, 会等到需要结果时才执行. 一旦执行终止操作, 就会停止
  • Stream 一旦执行了终止操作, 就不能调用其他中间操作or终止操作了

执行流程

  • Stream 的实例化
  • 一系列中间操作
  • 执行终止操作

步骤一 Stream 实例化

  1. 通过集合创建 Stream
java 复制代码
// 创建 Stream方法一: 通过集合
@Test
public void test1(){
    List<Employee> list = EmployeeData.getEmployees();
    // 集合. 就能看到有stream方法可以调用
    // default Stream<E> stream() : 返回一个顺序流
    Stream<Employee> stream = list.stream();
    // default Stream<E> parallelStream() : 返回一个并行流
    Stream<Employee> stream1 = list.parallelStream();
    System.out.println(stream);
    System.out.println("===============");
    System.out.println(stream1);
}

运行效果: (输出的是Stream对象)

  1. 通过数组来创建 Stream
java 复制代码
// 创建 Stream方法二: 通过数据
@Test
public void test2(){
    // 调用Arrays类的 static<T> Stream<T> stream(T[] array)
    Integer[] arr = new Integer[]{1,2,3,1,1,4};
    Stream<Integer> stream = Arrays.stream(arr); // 可以清楚看到处理的数据类型 Integer, Stream 不存储元素
    System.out.println(stream);
    System.out.println("=======================");
    int[] arr1 = new int[]{1,2,31,21,1};
    IntStream stream1 = Arrays.stream(arr1); // 具体指明操作什么类型的数据(看传的是什么数据) 有 IntStream, DoubleStream ...
    System.out.println(stream1);
}

运行效果:

  1. 使用 stream的 of() 方法来创建
java 复制代码
// 创建 Stream方法三: 通过Stream的of()
@Test
public void test3(){
    Stream<String> stream = Stream.of("aa", "bb", "AA", "CC");
    System.out.println(stream);
}

运行效果:

步骤二 中间操作

常见的中间操作

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

  1. 筛选与切片
java 复制代码
// 1. 筛选与切片
@Test
public void test1(){
    // filter() 接受lambda, 从流中排除某些元素 (支持多条件 用 && || 连接)
    // 查询员工表中薪资大于 7000的员工信息
    List<Employee> list = EmployeeData.getEmployees();
    Stream<Employee> stream = list.stream();
    stream.filter(emp -> emp.getSalary() > 7000).forEach(System.out::println); // forEach() 相当于一个终止条件(不然不会执行该语句&输出)
    System.out.println();
​
    // limit(n), 截断流, 使元素
//        stream.limit(2).forEach(System.out :: println); // 这种做法是错误的, 因为上面的stream已经执行终止操作
    // 所以不能调用其他中间操作or终止操作, 只能新造一个
    list.stream().limit(4).forEach(System.out :: println); // 限制输出 前几个数据 (可以连续操作)
    System.out.println();
    // skip(n) 跳过元素, 返回一个除前n个元素的流; 若流中元素不足n个, 则会返回一个空流
    list.stream().skip(2).forEach(System.out :: println);
    System.out.println();
    // distinct() 去重, 通过流生成元素的 hashCode() 和 equals() 去除重复元素 (在Employee.java 里改写了)
    // 原有的方法会认为下面几条数据是一致的
    list.add(new Employee(18, 2000.0,2,"sam"));
    list.add(new Employee(18, 2000.0,2,"sam"));
    list.add(new Employee(18, 2000.0,2,"sam"));
​
    list.stream().distinct().forEach(System.out :: println);
}

执行效果:

映射

java 复制代码
// 2 映射
@Test
public void test2(){
    // map(Function f) - 接受一个函数作为参数. 将元素转换成其他形式or提取信息
    // 小写 -> 大写
    List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
    list.stream().map(str -> str.toUpperCase()).forEach(System.out :: println);
    // map 里 也可以写成 str :: toUpperCase
    System.out.println();
    // 获取 员工姓名长于3的员工
    List<Employee> employees = EmployeeData.getEmployees();
    employees.stream().filter(emp -> emp.getName().length() > 3).forEach(System.out :: println);
    System.out.println();
    // 获取 员工姓名长度大于3的员工姓名
    employees.stream().filter(emp -> emp.getName().length() > 3).map(emp -> emp.getName()).forEach(System.out :: println);
    // 先过滤后映射
}

执行效果:

stream流

排序

java 复制代码
// 3 排序
@Test
public void test3(){
    Integer[] arr = new Integer[]{32,3213,1231,123};
    String[] arr1 = new String[]{"GG", "DD", "MM", "SS", "JJ"};
    // sorted() 自然排序 (默认升序排序)
    Arrays.stream(arr).sorted().forEach(System.out :: println);
    System.out.println(Arrays.toString(arr)); // 是原来本身, stream流并不会改变容器本身
    List<Employee> list = EmployeeData.getEmployees();
//        list.stream().sorted().forEach(System.out :: println); // 因为Employee没有实现Comparable接口, 所以会报错
//        报错: java.lang.ClassCastException: java8.data.Employee cannot be cast to java.lang.Comparable
    // sorted(Comparator com) 定制排序 (lambda表达式 返回的bool值)
    list.stream().sorted((e1, e2) -> e1.getAge() -e2.getAge()).forEach(System.out::println);// 升序
    System.out.println();
    Arrays.stream(arr1).sorted((s1, s2) -> -s1.compareTo(s2)).forEach(System.out :: println); // 降序排列(-, 默认升序)
}

执行效果:

对数据操作的例子

java 复制代码
// 匹配和查找
@Test
public void test1(){
    // allMatch(Predicate p) - 检查是否匹配所有元素 Predicate 里面的方法为 boolean test(T t);
    // 是否所有的员工的年龄都大于18
    List<Employee> list = EmployeeData.getEmployees();
    System.out.println(list.stream().allMatch(emp -> emp.getAge() > 18)); // false
    // anyMatch(Predicate p)
    // 是否存在员工年龄大于18 (存在一个就返回true)
    System.out.println(list.stream().anyMatch(emp -> emp.getAge() > 18)); // true
    // findFirst 返回第一个元素 返回的是Optional
    System.out.println(list.stream().findFirst().get()); // .get() 返回第一个参数
}
​
// 统计数字特征
@Test
public void test2() {
    // count 返回流元素的总个数
    // 统计工资超过 7000元 员工个数
    List<Employee> list = EmployeeData.getEmployees();
    System.out.println(list.stream().filter(emp -> emp.getSalary() > 7000).count());
    // max() 返回流中的最大值
    // 返回最高工资的钱数 map 取其中的内容(列)
    // 要先map 然后 max 因为 max 是一个终止操作
    System.out.println(list.stream().map(emp->emp.getSalary()).max((e1, e2)-> Double.compare(e1, e2)).get()); // .get() 是取出Optional中的元
    // min() 同理
    // forEach(Customer c) 内部迭代
    list.stream().forEach(System.out :: println); // 相当于将方法作为参数传入 accpt 函数中、
​
    // 针对于集合, jdk8 新增了一个遍历的方法
    list.forEach(System.out::println);
}

执行结果(test1()):

test2():

  1. 规约
java 复制代码
// 规约
@Test
public void test3(){
    // reduce(T identity, BinaryOperator) 将流中的元素 反复结合起来
    // 计算1-10自然数的和
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    System.out.println(list.stream().reduce(0, (x1, x2) -> x1 + x2)); // 第一个参数为初始种子
    // reduce(BinaryOperator) 返回 Optional<T>
    List<Employee> employees = EmployeeData.getEmployees();
    System.out.println(employees.stream().map(emp -> emp.getSalary()).reduce((s1, s2) -> Double.sum(s1, s2))); // 可以在最后用 .get() 取出Optional里的值
    // 当然上面lambda表达式中可以替换为 Double::sum
}

执行结果:

  1. 收集

Collector接口中方法的实现决定了如何对流执行收集的操作(如: 收集到 List, Set, Map ...)

toList, toSet, toCollection, counting 统计流中元素数量 等... 功能

java 复制代码
// 收集 (把结果放回原处)
@Test
public void test4() {
    // collect() 将流转换成其他形式
    List<Employee> list = EmployeeData.getEmployees();
    // 查找工资大于6000的员工, 结果返回一个List或Set Collectors.tolist() / Collectors.toset()
    List<Employee> list1 = list.stream().filter(emp -> emp.getSalary() > 6000).collect(Collectors.toList());
    list1.forEach(System.out :: println); // 这里的list1 已经是处理好之后的数据了, 然后 forEach是遍历列表, 打印
}

执行结果:

步骤三 终止查询

.max() .min() .forEach() ... 等为最后对数据的终止条件, 此时 stream流就不能继续使用了, 需要用的话 需要重新创建 ~

相关推荐
面试鸭几秒前
离谱!买个人信息买到网安公司头上???
java·开发语言·职场和发展
沈询-阿里42 分钟前
java-智能识别车牌号_基于spring ai和开源国产大模型_qwen vl
java·开发语言
AaVictory.1 小时前
Android 开发 Java中 list实现 按照时间格式 yyyy-MM-dd HH:mm 顺序
android·java·list
Yaml41 小时前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
LuckyLay1 小时前
Spring学习笔记_27——@EnableLoadTimeWeaving
java·spring boot·spring
向阳12181 小时前
Dubbo负载均衡
java·运维·负载均衡·dubbo
Gu Gu Study2 小时前
【用Java学习数据结构系列】泛型上界与通配符上界
java·开发语言
小码编匠2 小时前
一款 C# 编写的神经网络计算图框架
后端·神经网络·c#
WaaTong2 小时前
《重学Java设计模式》之 原型模式
java·设计模式·原型模式