hi,我是逸尘,一起学java吧
得益于Lambda所带来的函数式编程,引入了一个全新的Stream流概念(就是都基本使用lambda的形式)。
流处理
我们首先理解什么是流处理,它类似于sql语句,可以执行非常复杂的过滤,映射,查找,收集等功能,且代码很少,但是可读性不高。字如其名,它的处理如同流淌的水一样,或者可以理解为流水线一样。
Stream流
Stream流也是流处理的一种,大多数流处理都是在Stream接口处理的,它是一个泛接口,所以它可以操作的元素是任意对象,他的操作可以用lambda去书写(推荐)。
生成Stream流
Stream操作集合和数组的第一步是得到(生成)Stream流。
在Collection接口默认方法是stream()生成流。
在数组中使用Arrays.stream(数组) /或Stream.of(数组);
中间操作方法
其次我们就可以使用中间操作来处理这些元素对象。
这里举出一些常见的API
- forEach : 逐一处理(遍历)
- count:统计个数
- filter : 过滤元素 【数据过滤】
- distinct:去除重复元素 【数据过滤】
- limit : 取前几个元素 【数据过滤】
- skip : 跳过前几个 【数据过滤】
- map : 加工方法 【数据映射】
- allMatch:判断流中的元素是否会全部符合某一个条件 【数据查找】
- concat:合并流
终结操作方法
终结操作方法调用以后流就无法使用了它是流的最后一个过程。
常见的有API有
单独保存的操作方法
- collect() 方法配合collectors类将流的结果进行保存
处了stream流本身的方法我们还有两个可以协助流操作的类
Collectors类
collectors是一个收集器类,可以将Stream流对象进行封装,归集,分组,是数据的收集,筛选出特殊的数据,可以复杂的统计。
1.toList()将流元素封装到List集合 toSet() toMap()类似
2.toCollection(Supplier<C> collectionFactory) 将流中的元素收集到指定类型的集合中的方法
即一个类型为 Supplier<C>
的函数式接口,其中 C
是要创建的集合类型。例如,如果我们想要创建一个 LinkedList
集合,可以这样使用该方法:
List<Integer> list = Stream.of(1, 2, 3, 4, 5).collect(Collectors.toCollection(LinkedList::new));
3.groupingBy(Function<? super T, ? extends K> classifier)
- 一个函数式接口
classifier
,表示如何对流中的元素进行分类。
例如,我们有一个字符串列表,并希望按照字符串长度分组:
List<String> list = Arrays.asList("apple", "banana", "peach", "grape","pear");
Map<Integer, List<String>> map = list.stream().collect(Collectors.groupingBy(String::length));
在上面的代码中,我们使用 String::length 函数式接口将字符串转换为它的长度,并将其作为分类键。运行结果如下:
{4=[pear], 5=[apple, peach,grape], 6=[banana]}
需要注意的是,groupingBy() 方法返回的是一个 Map 对象,其中键是分类键.
Collectors.groupingBy() 方法还提供了第二个参数 downstream,用于进一步对分组的结果进行处理。例如,我们可以使用 Collectors.counting() 方法统计每个分组中元素的数量:
Map<Integer, Long> map = list.stream().collect(Collectors.groupingBy(String::length, Collectors.counting()));
上面的代码中,我们使用 Collectors.counting() 方法作为 downstream 参数,统计了每个分组中元素的数量,并将结果封装为 Long 类型。运行结果如下:
{4=1, 5=3, 6=1}
4.toConcurrentMap
将流中的元素收集到一个并发 Map
中的方法
ConcurrentMap<Integer, String> concurrentMap = Stream.of("a", "b", "c").collect(Collectors.toConcurrentMap(String::length,Function.identity()));
在上面的例子中,我们使用 toConcurrentMap() 方法创建了一个并发 Map,它将字符串的长度作为键,字符串本身作为值。具体来说,这个方法接受两个参数:
一个函数式接口 keyMapper,表示如何将流中的元素转换成键;
一个函数式接口 valueMapper,表示如何将流中的元素转换成值。
对于上述例子中的流,String::length 函数式接口将字符串转换成它的长度,而 Function.identity() 函数式接口则将字符串映射成它本身。因此得到的结果为:
{1=a, 2=b, 3=c}
5.以及averagingDouble计算元素平均值,maxBy返回符合条件的最大值,joining()按顺序将元素连接成一个String类型数据,counting()统计个数等等
optional类
这是一个容器类
它的主要功能是针对NullpointerException空指针异常做处理,可以保证保存的值不为null。
of()返回一个value值等于参数的optional实例
ofNullable()是返回一个value值等于非null参数的optional实例
filter()是给定条件值匹配
empty()是静态方法,返回一个空值的optional实例
案例
我们上面的中间操作,其实是对我们的数据源进行加工。
这里我们简单做了一个去重
package com.yd.yc;import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;public class Thirteen {public static void main(String[] args) {List<Integer> list = Arrays.asList(6,88,4,5,22,22,6,66,7);//原数据printeach(list);//获取stream,去重,收集器类重新封装List<Integer> collect = list.stream().distinct().collect(Collectors.toList());printeach(collect);}//遍历方法private static void printeach(List<Integer> list) {System.out.println("集合内容"+list);//逐一处理(遍历)里面是lambdalist.stream().forEach(n-> System.out.println(n+""));}
}
某个公司的部门,分为开财务部门和开发部门,现在需要进行月中数据结算。
创建一张员工
部门 | 姓名 | 年龄 | 月工资 | 性别 |
---|---|---|---|---|
开发部 | 张三 | 28 | 15000 | 男 |
开发部 | 李四 | 35 | 20000 | 女 |
开发部 | 王五 | 29 | 18000 | 男 |
财务部 | 赵六 | 33 | 16000 | 女 |
财务部 | 刘七 | 30 | 17000 | 男 |
财务部 | 陈八 | 27 | 14000 | 女 |
对应的是我们的员工实体类
public class Employee { private String name; private int age; private double monthlySalary; private String gender; private String department; public Employee(String name, int age, double monthlySalary, String gender, String department) { this.name = name; this.age = age; this.monthlySalary = monthlySalary; this.gender = gender; this.department = department; } public String getName() { return name; } public int getAge() { return age; } public double getMonthlySalary() { return monthlySalary; } public String getGender() { return gender; } public String getDepartment() { return department; }
}
分别筛选出2个部门的最高工资的员工信息,封装成优秀员工对象topperformer
方案A
package com.yd.yc;import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;public class EmployeeTest {public static void main(String[] args) {Employee employee1 = new Employee("张三", 28, 15000, "男", "开发部");Employee employee2 = new Employee("李四", 35, 20000, "女", "开发部");Employee employee3 = new Employee("王五", 29, 18000, "男", "开发部");Employee employee4 = new Employee("赵六", 33, 16000, "女", "财务部");Employee employee5 = new Employee("刘七", 30, 17000, "男", "财务部");Employee employee6 = new Employee("陈八", 27, 44000, "女", "财务部");//测试//System.out.println(employee1.getName()); // 输出:张三//System.out.println(employee2.getMonthlySalary()); // 输出:20000.0ArrayList<Employee> employeeList = new ArrayList<>();employeeList.add(employee1);employeeList.add(employee2);employeeList.add(employee3);employeeList.add(employee4);employeeList.add(employee5);employeeList.add(employee6);//A方案//filter是过滤找到符合条件的元素//Collectors.maxBy去返回符合条件的最大值,Comparator.comparing(param),param : 这个参数是Function函数式对象,默认大,Comparator.reverseOrder()默认倒序Optional<Employee> result = employeeList.stream().filter(e -> "开发部".equals(e.getDepartment())).collect(Collectors.maxBy(Comparator.comparing(Employee::getMonthlySalary)));//返回一个实体类对象Employee employee = result.get();System.out.println(employee.getName());//第二种写法Employee resultOne= employeeList.stream().filter(e -> "财务部".equals(e.getDepartment())).max((o1, o2) -> Double.compare(o1.getMonthlySalary(), o2.getMonthlySalary())).get();//必须重写toString才可以有内容System.out.println(resultOne);System.out.println(resultOne.getMonthlySalary());//包装在一个优秀员工里List<Employee> topEmployees = new ArrayList<>();topEmployees.add(resultOne);topEmployees.add(employee);System.out.println(topEmployees);}
}
方案B
//B方案Map<String, List<Employee>> groupedByDepartment = employeeList.stream()//分组.collect(Collectors.groupingBy(Employee::getDepartment));List<Employee> topEmployees = new ArrayList<>();for (List<Employee> departmentEmployees : groupedByDepartment.values()) {Employee topEmployee = departmentEmployees.stream().max(Comparator.comparingDouble(Employee::getMonthlySalary))//注意,如果一个部门没有员工,那么这个方法将返回null//该方法在给定的流中找不到元素时返回一个默认值。.orElse(null);if (topEmployee != null) {topEmployees.add(topEmployee);}}System.out.println(topEmployees);
我们要注意的是stream流是方便操作集合/数组的手段,集合/数组才是开发中的目的。