函数式接口
具有单个抽象方法的接口被称为“函数式接口”,函数式接口的实例被称为“函数对象”,表示函数和要采取的动作。
六大基础函数式接口:
函数式接口 | 名称 | 用途 | 方法 |
---|---|---|---|
Consumer<T> |
消费型接口 | 操作类型 T 的对象 |
void accept(T t) |
Supplier<T> |
供给型接口 | 返回类型为 T 的对象 |
T get() |
Function<T, R> |
函数型接口 | 操作类型为 T 的对象,返回类型为 R 的对象 |
R apply(T t) |
Preicate<T> |
判断型接口 | 判断类型为 T 的对象是否满足约束,并返回 boolean 值 |
boolean test(T t) |
unaryOperator<T> |
一元运算接口 | 操作类型为 T 的对象,返回类型为 T 的对象。常见的操作有:自增、自减和按位取反等 |
T apply(T t) |
BinaryOperator<T, T> |
二元运算接口 | 操作两个类型为 T 的对象,返回类型为 T 的对象。常见的操作有:两数相加和两数相减等 |
T apply(T t1, T t2) |
注意:Function
接口参数和返回值不一致,Operator
接口参数和返回值一致。
java.util.Function
包中共有 43 个接口,很多接口都是上述基础接口派生而来的,例如,BiConsumer(T, U)
和 BiFunction<T, U, R>
等。
Lambda 表达式
创建函数式接口的实例可以用 Lambda 表达式创建。
Lambda 表达式的语法总结:
->
的左边是形参列表,参数类型可以省略,形参只有一个,()
也可以省略。->
的右边是 Lambda 体,如果只有一条语句,{}
可以省略,return 关键字也可以省略。
Lambda 表达式的一些示例:
Consumer<Integer> consumer = a -> System.out.println(a);
Supplier<String> supplier = () -> "hello world!";
Function<String, Integer> function = a -> a.length();
Predicate<String> predicate = a -> a.length() > 2;
Comparator<Integer> comparator = (a, b) -> Integer.compare(b, a);
方法引用
方法引用是基于 Lambda 的进一步刻画。格式:类(或对象):: 方法
,有三种情况。
首先,准备 Employee 类:
public class Employee {
private int id;
private String name;
private int age;
private double salary;
public Employee(int id) {
this.id = id;
}
public Employee(int id, String name) {
this.id = id;
this.name = name;
}
public Employee(int id, String name, int age, double salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
}
情况一:对象 :: 非静态方法
函数式接口中的抽象方法 a 和实现 a 时调用的对象方法 b 的参数列表和返回值类型都相同。该方法是非静态方法,需要对象调用。
例如:
Employee emp = new Employee(1000, "Tom", 22, 11000.0);
Supplier<String> supplier1 = new Supplier<String>() {
@Override
public String get() {
return emp.getName();
}
};
Supplier<String> supplier2 = () -> emp.getName(); // Lambda 表达式
Supplier<String> supplier3 = emp::getName; // 方法引用
以上三种实现 Suppler
接口的方式等效。
再例如,根据字符串的长度对字符串 List 进行排序,代码如下:
List<String> words = Arrays.asList("abd", "ab", "ac");
words.sort(new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.length() - b.length();
}
});
上述代码可以简化为:
words.sort(words, (a,b) -> a.length() - b.length);
还可以简化为:
words.sort(Comparator.comparingInt(String::length));
情况二:类 :: 静态方法
函数式接口中的抽象方法 a 和实现 a 时调用的类静态方法 b 的参数列表和返回值类型都相同。该方法是非静态方法,需要对象调用。
例如:
Comparator<Integer> comparator1 = (a, b) -> Integer.compare(a, b);
Comparator<Integer> comparator2 = Integer::compare;
情况三:类 :: 非静态方法
函数式接口中的抽象方法 a 与其内部实现时调用的对象的某个方法 b 的返回值相同。同时,抽象方法 a 有 n 个参数,方法 b 有 n - 1 个参数,a 的第一个参数是方法 b 的调用者。
例如:
Comparator<Integer> comparator1 = (a, b) -> a.compareTo(b);
Comparator<Integer> comparator2 = Integer::compareTo;
情况四:构造器引用
构造器引用格式:类名 :: new。具体调用类中的哪一个构造器取决于函数式接口中的抽象方法的形参列表。
例如:
Function<Integer, Employee> function1 = (id) -> new Employee(id);
Function<Integer, Employee> function2 = Employee::new;
再例如:
BiFunction<Integer, String, Employee> biFunction1 = (id, name) -> new Employee(id, name);
BiFunction<Integer, String, Employee> biFunction2 = Employee::new;
构造器引用的数组形式:
Function<Integer, Employee[]> func1 = (a) -> new Employee[a];
Function<Integer, Employee[]> func2 = Employee[]::new;
Stream 流
Stream 是 Java 8 中处理集合的关键抽象概念,使用 Stream API 对几何数据进行操作,就类似于使用 SQL 执行的数据库查询。Stream 是数据渠道,用于操作数据集合。
注意:
- Stream 不会自己存储数据。
- Stream 不会改变原数据。它们会返回一个持有结果的新 Stream。
- Stream 一旦终止操作就不能再调用其它中间操作了。
Stream 操作的三个步骤:
- 创建 Stream。
- 中间操作,例如排序、查找、过滤、映射、遍历等。
- 终止操作。
创建 Stream
准备数据:
List<Employee> employees = new ArrayList<>();
Employee emp1 = new Employee(1, "Tom", 23, 4500.0);
employees.add(emp1);
Employee emp2 = new Employee(2, "Mary", 26, 6500.0);
employees.add(emp2);
employees.add(new Employee(3, "Jerry", 19, 3500.0));
employees.add(new Employee(4, "Nancy", 35, 11000));
创建 Stream 有三种方式:
- 方式一:通过集合获取 Stream。
- 方式二:通过数组获取 Stream。
- 方式三:
Stream.of(T... values)
方法。
例如:
// 方式一:通过集合获取 Stream
Stream<Employee> stream = employees.stream();
// 方式二:通过数组获取 Stream
IntStream intStream = Arrays.stream(new int[]{1, 2, 3, 4, 5});
// 方式三:Stream.of(T... values) 方法
Stream<Employee> employeeStream = Stream.of(emp1, emp2);
中间操作
常用的方法如下:
filter(Predicate<? super T> predicate)
:过滤。<R> Stream<R> map(Function<? super T, ? extends R> mapper)
:映射。Stream<T> limit(long maxSize)
:截断前 n 个元素。Stream<T> skip(long n)
:跳过前 n 个元素。distinct()
:根据对象的hashcode()
和equals()
方法去重。Stream<T> sorted(Comparator<? super T> comparator)
:排序。
示例代码:
// 查询集合中员工薪资大于 7000 的员工信息
employees.stream().filter(a -> a.getSalary() > 7000).forEach(System.out::println);
// 查询集合中员工薪资大于 7000 的员工的 id
employees.stream().filter(a -> a.getName().length() > 3).map(Employee::getId).forEach(System.out::println);
// 只取前 2 项
employees.stream().limit(2).forEach(System.out::println);
// 跳过前 2 项
employees.stream().skip(2).forEach(System.out::println);
// 去重
employees.stream().distinct().forEach(System.out::println);
// 根据 employee 名字的长度排序
employees.stream().sorted(Comparator.comparingInt(a -> a.getName().length())).forEach(System.out::println);
终止操作
终止操作分为三种:
- 匹配与查找。
- 规约。
- 收集。
匹配与查找
方法:
anyMatch()
:是否有一个匹配。
allMatch()
:是否全部匹配。
findFirst()
:查找第一个。
count()
:计数。
max()
:最大值。
min()
:最小值。
forEach()
:遍历。
例如:
// 是否有员工的薪资大于 7000
employees.stream().anyMatch(employee -> employee.getSalary() > 7000);
// 是否所有员工的年龄都大于 20
employees.stream().allMatch(employee -> employee.getAge() > 20);
// 年龄最大的员工的名字
employees.stream().max(Comparator.comparingInt(Employee::getAge)).get().getName();
// 遍历打印所有员工的信息
employees.forEach(System.out::println);
规约
Optional<T> reduce(BinaryOperator<T> accumulator)
将值反复结合起来得到一个值。
例如:
// 计算所有员工薪资的总和
Double sum = employees.stream().map(Employee::getSalary).reduce(Double::sum).get();
收集
collect(Collectors.toList)
:将结果收集起来转成 List。
例如,int[]
数组转成 List<Integer>
集合:
int[] arr = new int[]{1, 2, 3, 4, 5};
List<Integer> list = Arrays.stream(arr).boxed().collect(Collectors.toList());
标签:Java,Stream,employees,接口,Employee,new,id,Lambda
From: https://www.cnblogs.com/code-qin/p/18108374