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 实例化
- 通过集合创建 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对象)
- 通过数组来创建 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);
}
运行效果:
- 使用 stream的 of() 方法来创建
java
// 创建 Stream方法三: 通过Stream的of()
@Test
public void test3(){
Stream<String> stream = Stream.of("aa", "bb", "AA", "CC");
System.out.println(stream);
}
运行效果:
步骤二 中间操作
常见的中间操作
多个中间操作可以连接起来形成一个流水线 , 除非流水线上触发终止操作 , 否则中间操作不会执行任何的处理, 而是在终止操作 时一次性全部处理
- 筛选与切片
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():
- 规约
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
}
执行结果:
- 收集
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流就不能继续使用了, 需要用的话 需要重新创建 ~