1、
Comparator接口:
@FunctionalInterface public interface Comparator<T> { int compare(T o1, T o2);
(1)匿名内部类
@Test public void test1() { Comparator<Integer> com = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return Integer.compare(o1, o2); } }; }
(2)Lambda表达式
@Test public void test2(){ Comparator<Integer> com=(o1,o2)->Integer.compare(o1,o2); }
(3)案例(获取年龄大于35的员工信息)
常规方式:
定义一个员工类,遍历所有员工的信息,将年龄大于35的员工的信息存储到新的集合中。但是,如果把需求改为获取工资大于5000的员工信息,只需要改变的是程序的判断条件,这就造成了代码的冗余
设计模式:策略设计模式
新建一个员工类
@Data @AllArgsConstructor public class Employee { private int id; private String name; private int age; private double salary; }
创建一个接口,该接口定义一个用于过滤的方法
@FunctionalInterface public interface MyPredicate<T> { public boolean test(T t); }
创建两个接口的实现类:实现类重写接口的方法分别过滤年龄和工资
工资
public class FilterEmployeeForSalary implements MyPredicate<Employee> { @Override public boolean test(Employee t) { return t.getSalary() >= 5000; } }
年龄
public class FilterEmployeeForAge implements MyPredicate<Employee>{ @Override public boolean test(Employee t) { return t.getAge() <= 35; } }
测试类:
添加员工的信息
定义过滤方法,该方法的第一个参数是一个Employee类型的List集合,第二个参数是一个接口类型的,也就是说可以传递该接口的所有实现类,也就是说没有将方法写死,方法的灵活度更高了
第三个方法是测试类,传递员工的信息和接口的实现类,返回的是过滤后的结果
public class TestLambda { List<Employee> emps = Arrays.asList( new Employee(101, "张三", 18, 9999.99), new Employee(102, "李四", 59, 6666.66), new Employee(103, "王五", 28, 3333.33), new Employee(104, "赵六", 8, 7777.77), new Employee(105, "田七", 38, 5555.55) ); public List<Employee> filterEmployee(List<Employee> list, MyPredicate<Employee> myPredicate) { List<Employee> employees = new ArrayList<>(); for (Employee employee : list) { if (myPredicate.test(employee)) { employees.add(employee); } } return employees; } @Test public void test() { List<Employee> employeeList = filterEmployee(emps, new FilterEmployeeForAge()); for (Employee employee : employeeList) { System.out.println(employee); } System.out.println("-------------------"); List<Employee> list = filterEmployee(emps, new FilterEmployeeForSalary()); for (Employee employee : list) { System.out.println(employee); } }
测试结果:
Employee(id=101, name=张三, age=18, salary=9999.99) Employee(id=103, name=王五, age=28, salary=3333.33) Employee(id=104, name=赵六, age=8, salary=7777.77) ------------------- Employee(id=101, name=张三, age=18, salary=9999.99) Employee(id=102, name=李四, age=59, salary=6666.66) Employee(id=104, name=赵六, age=8, salary=7777.77) Employee(id=105, name=田七, age=38, salary=5555.55)
匿名内部类方式
public List<Employee> filterEmployee(List<Employee> list, MyPredicate<Employee> myPredicate) { List<Employee> employees = new ArrayList<>(); for (Employee employee : list) { if (myPredicate.test(employee)) { employees.add(employee); } } return employees; } @Test public void test() { List<Employee> list = filterEmployee(emps, new MyPredicate<Employee>() { @Override public boolean test(Employee employee) { return employee.getSalary() < 7000; } }); for (Employee employee : list) { System.out.println(employee); } }
匿名内部类方式与策略模式相比,少了对实现类的书写,但是匿名内部类的方式还是不够简洁
Lambda表达式
public List<Employee> filterEmployee(List<Employee> list, MyPredicate<Employee> myPredicate) { List<Employee> employees = new ArrayList<>(); for (Employee employee : list) { if (myPredicate.test(employee)) { employees.add(employee); } } return employees; } @Test public void test() { List<Employee> list = filterEmployee(emps,(e)->e.getSalary()<7000); for (Employee employee : list) { System.out.println(employee); } }
与函数时接口的方式相比,Lambda表达式的方式使得代码更加简洁
最简洁的方式:Stream API
@Test public void test() { emps.stream() .filter((e)->e.getSalary()>=5000) .forEach(System.out::println); }
此种方式只需要一个实体类
2、函数式接口
(1)定义一个函数式接口
@FunctionalInterface public interface MyFun { public Integer getValue(Integer num); }
(2)定义方法并测试
public class TestLambda { public Integer operation(Integer num, MyFun mf) { return mf.getValue(num); } @Test public void test() { Integer num = operation(100, (x) -> x * x); System.out.println(num); System.out.println(operation(200, (y) -> y + 200)); } }
方法一的参数是一个接口类型的,该方法调用了接口内的方法
方法二是测试方法,Lambda表达式需要传递一个参数
3、Lambda表达式的应用
(1)排序
函数式接口
int compare(T o1, T o2);
调用函数式接口的方法
public static <T> void sort(List<T> list, Comparator<? super T> c) { list.sort(c); }
测试类
List<Employee> emps = Arrays.asList( new Employee(101, "张三", 18, 9999.99), new Employee(102, "李四", 59, 6666.66), new Employee(103, "王五", 28, 3333.33), new Employee(104, "赵六", 8, 7777.77), new Employee(105, "田七", 38, 5555.55) ); @Test public void test1() { Collections.sort(emps, (e1, e2) -> { if (e1.getAge() == e2.getAge()) { return e1.getName().compareTo(e2.getName()); } else { return Integer.compare(e1.getAge(), e2.getAge()); } }); for (Employee emp : emps) { System.out.println(emp); } }
(2)处理字符串
定义一个函数式接口
@FunctionalInterface public interface MyFunction { public String getValue(String str); }
定义字符串处理方法和测试方法
public class TestLambda { public String strHandler(String str, MyFunction mf){ return mf.getValue(str); } @Test public void test2(){ String trimStr = strHandler(" 你好 ", (str) -> str.trim()); System.out.println(trimStr); String upper = strHandler("qqqqeeeabc", (str) -> str.toUpperCase()); System.out.println(upper); String newStr = strHandler("1234556", (str) -> str.substring(2, 5)); System.out.println(newStr); } }
(3)数据处理
函数值接口
public interface MyFunction2<T, R> { public R getValue(T t1, T t2); }
测试类
public class TestLambda { public void op(Long l1, Long l2, MyFunction2<Long, Long> mf){ System.out.println(mf.getValue(l1, l2)); } @Test public void test3(){ op(100L, 200L, (x, y) -> x + y); op(100L, 200L, (x, y) -> x * y); } }
总结
匿名内部类能够避免接口的实现类的书写,但是还可以通过Lambda表达式进一步简化,Lambda表达式的应用需要借助于函数式接口,JDK中自带的有一些函数式接口,可以拿来直接使用
Lambda表达式的箭头左边用来书写输入参数,右边是函数式接口方法的实现