lambda表达式
对于面向对象对象语言中 lambda表达式其实是不重要的
如果你有了解过JavaScript、你一定对函数式编程比较熟悉(Js太恶心啦!!! 用的时候看不了规则根本不知道怎么写)
多说无益、先看效果:首先我们先假设有这么一个场景要实现一个数组排序 给定一个数组、将数组按指定要求(正序/倒序)排序
在Java
中我们可以直接使用Arrays.sort()
调用JDK中 的方法进行排序(但该方法只提供了正序排序的实现)
1 2 3 4 5 6 7 8 9
| public class Sort {
public static void main(String[] args) { int[] nums = new int[]{1,3,5,25,7,23,7,34,8,12,5,3,71,236,12}; Arrays.sort(nums); System.out.println(Arrays.toString(nums)); } }
|
当然sort()方法也可以实现倒序排序、只不过有点不一样
1 2 3 4 5 6 7 8 9 10
| public class Sort {
public static void main(String[] args) { Integer[] nums = new Integer[]{1,3,5,25,7,23,7,34,8,12,5,3,71,236,12}; Arrays.sort(nums, Collections.reverseOrder()); System.out.println(Arrays.toString(nums)); } }
|
ok , 通过这两个简单方法就可以实现我们的功能、但这并不是本问介绍的重点、重点是我们要通过这个案例来学习了解到lambda表达式、并且间接的感受Java函数式编程思想
接下来我们就来实现一个简易版本的Arrays.sort(T[]nums,Comparable)
首先确定想要的效果、T[]nums 确定一个任何对象类型的数组、传入一个比较器接口类、该接口仅仅做一件事情就是帮我比较两个数大小,叫做比较器,当我们的排序数据换成其他对象时候、原始的+
与-
已经不能满足我们的需求了、比如比较String 大小 在String中存在CompareTo方法比较两个字符串大小,因此为满足其他复杂数据类型也能够排序、引入比较器来自定义比较两个数据大小尤为重要
先定义比较器、为了不予Jdk本身接口(Comparator)冲突我取名叫ShenComparator.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @FunctionalInterface public interface ShenComparator<T> {
int compare(T o1,T o2); boolean equals(Object obj);
}
|
这里的比较器唯一的规则就是 比较大小结果是大于0还是小于0,举个例子 假设我们需要比较两个Integer类型数字、可以直接将前面数字减去后面的数字相减
1 2 3 4 5 6
|
int compare(Integer o1,Integer o2) { return o1 - o2; }
|
ok、有了比较器也还先别着急、 先用最简易版本的数字排序来熟悉一下排序的过程、这里为了简单我直接用快速排序算法来实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| public class Utils {
public static void sort(int[] nums) { quickSort(nums,0,nums.length - 1); }
private static void quickSort(int[] nums,int left,int right) { if (left >= right) return; int pivot = partition(nums, left, right); quickSort(nums, left, pivot - 1); quickSort(nums, pivot + 1, right); }
private static void swap(int[] nums,int x, int y) { int mid = nums[x]; nums[x] = nums[y]; nums[y] = mid; }
private static int partition(int[] nums, int left, int right) { int i = left, j = right; while (i < j) { while (i < j && nums[j] >= nums[left]) j--; while (i < j && nums[i] <= nums[left]) i++; swap(nums, i, j); } swap(nums, i, left); return i; } }
|
现在已经实现了基本的排序功能、但是只针对于int[]数组、而且不支持随意切换正序还是倒序,现在测试一下
1 2 3 4 5 6 7 8 9
| public class Main {
private static final int[] nums = {43,1,56,7,23,12,46,23,8,9,4,23,56,30,23,5}; public static void main(String[] args) { Utils.sort(nums); System.out.println(Arrays.toString(nums)); } }
|
可见基本上排序功能是已经实现了、但是还缺少本文想要描述的重点、假设现在不是一堆数字要排序、而是一堆’学生’对象想通过成绩、年龄或通过名称排序,那该怎么办,接下来就需要引入我们自定义的比较器接口
以及给我们的int[] 都替换成 T[] 来实现, 另外我们算法中对排序数比较大小的-
和+
号都可以用comparator.compare()方法来代替
加上比较器和泛型后
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| public class Utils {
public static <T> void sort(T[] nums, ShenComparator<T> comparator) { quickSort(nums,0,nums.length - 1,comparator); }
private static <T> void quickSort(T[] nums,int left,int right,ShenComparator<T> comparator) { if (left >= right) return; int pivot = partition(nums, left, right,comparator); quickSort(nums, left, pivot - 1,comparator); quickSort(nums, pivot + 1, right,comparator); }
private static <T> void swap(T[] nums,int x, int y) { T mid = nums[x]; nums[x] = nums[y]; nums[y] = mid; }
private static <T> int partition(T[] nums, int left, int right,ShenComparator<T> comparator) { int i = left, j = right; while (i < j) { while (i < j && comparator.compare(nums[j],nums[left]) >= 0) j--; while (i < j && comparator.compare(nums[i],nums[left]) <= 0) i++; swap(nums, i, j); } swap(nums, i, left); return i; } }
|
直接测试!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class Main {
public static void main(String[] args) {
Student[] students = new Student[]{ new Student(18,"lip",99F), new Student(19,"dabbe",45.5F), new Student(20,"miky",50.5F)};
Utils.sort(students, new ShenComparator<Student>() { @Override public int compare(Student o1, Student o2) { return (int)(o2.getScore() - o1.getScore()); } }); System.out.println(Arrays.toString(students)); } }
|
ok 这里可以看到、我们调用Utils.sort方法时new 了一个匿名类部类 并重写里面的 compare方法来实现传递比较器,这就是自定义排序规则的根本原因。
但是到现在我们还根本没有跟lambda
表达式沾边、是的、java8 以后匿名类部类可以改写简化成lambda表达式、但是有前提:
- 该接口中(ShenComparator)只能有一个抽象方法
- 也许还有。。。不记得了
用lambda表达式改写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class Main {
public static void main(String[] args) {
Student[] students = new Student[]{ new Student(18,"lip",99F), new Student(19,"dabbe",45.5F), new Student(20,"miky",50.5F)};
Utils.sort(students, (o1, o2) -> (int)(o2.getScore() - o1.getScore())); System.out.println(Arrays.toString(students)); } }
|
1
| Utils.sort(students,(a,b) -> b.getName().compareTo(a.getName()));
|
1
| Utils.sort(students,(a,b) -> a.getAge() - b.getAge());
|
备注
为了更容易理解、贴出类项目结构(额、好像没必要,算了贴都贴了反正就两个重要类ShenComparator 与 Utils)
建议查看java函数式编程的源码内容本文只是仿照他写的一个简易实现
我是怎么使用Lambda表达式的?
首先Lambda表达式主要用于集合的操作中
其中java Stream
流配合Lambda表达式对集合操作就非常便捷
比如:假设有一个包含员工信息的列表,我们希望按照部门对员工进行分组,并计算每个部门的平均工资。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public class Main {
@Data @AllArgsConstructor static class Employee { private String name; private String department; private double salary; }
public static void main(String[] args) {
List<Employee> list = Arrays.asList(new Employee("Alice", "HR", 50000), new Employee("Bob", "Engineering", 60000), new Employee("Charlie", "HR", 55000), new Employee("David", "Engineering", 70000), new Employee("Eva", "HR", 48000), new Employee("Frank", "Sales", 75000));
Map<String, Double> averageSalaryByDepartment = list.stream() .collect(Collectors.groupingBy(Employee::getDepartment, Collectors.averagingDouble(Employee::getSalary)));
averageSalaryByDepartment.forEach((department, salaryAVG) -> System.out.println("Department: " + department + ", Average Salary: " + salaryAVG)); }
}
|
另外使用lambda表达式除了要会查文档外更建议用Idea查看具体需要类型,如 我这里使用filter方法实现过滤功能、但看不明白需要传递参数是什么、可以通过idea查看、需要一个Predicate (断言)接口 、里面一个test方法 返回值为boolean类型 需要一个参数、知道这些信息就可以很轻松写出lambda表达式,不需要new 个匿名类部类再改写。