14.1 函数式接口
只包含一个抽象方法的接口称为函数式接口。 使用@FunctionalInterface 可以检查是否是函数式接口。Java内置了四大核心函数式接口以及一些其他接口。
函数式接口 | 用途 |
---|---|
Consumer消费型接口 | void accept(T t) |
Supplier供给型接口 | T get() |
Function |
R apply(T t) |
Predicate断言型接口 | boolean test(T t) |
// 函数式接口举例
@FunctionalInterface
public interface MyFunc<T> {
public T getValue(T t);
}
14.2 Lambda表达式
Lambda用于实例化函数式接口对象。以前用匿名实现类表示的现在都可以用Lambda表达式来写。
格式:(参数列表) -> {逻辑实现}
- 左侧:通过声明变量时即可推断出类型,因此可省略参数类型;若参数列表仅一个参数则可省略括号。
- 右侧:如果仅有一条语句可以省略 {}和return 。 ```java // 语法格式一:无参,无返回值 Runnable r1 = () -> { System.out.println(“Hello”); };
//语法格式二:有参数,但是没有返回值。
Consumer
//语法格式三:Lambda 若只需要一个参数时,参数的小括号可以省略
Consumer
//语法格式四:Lambda 可以有两个或以上的参数,多条执行语句,返回值
Comparator
//语法格式五:当 Lambda 体只有一条语句时,return 、{ } 都可以省略
Comparator
<a name="FhyrK"></a>
## 14.3 引用
<a name="tgM42"></a>
### 14.3.1 方法引用
当要传递给Lambda体的操作已经有实现的方法了,可以使用方法引用!这就要求实现接口抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致!
格式:使用操作符 “**::**” 将类(或对象) 与方法名分隔开来。
```java
// 等价例子1
Consumer<String> con = (x) -> System.out.println(x);
Consumer<String> con1 = System.out :: println;
// 等价例子2
Comparator<Integer> com = (x,y) -> Integer.compare(x,y);
Comparator<Integer> com1 = Integer::compare;
// 等价例子3
BiPredicate<String, String> bp = (x, y) -> x.equals(y);
BiPredicate<String, String> bp1 = String::equals;
14.3.2 构造器引用
可以把构造器引用赋值给定义的方法,要求构造器参数列表要与接口中抽象方法的参数列表一致!且方法的返回值即为构造器对应类的对象。
格式: ClassName::new
Function<Integer, MyClass> fun = (n) -> new MyClass(n);
Function<Integer, MyClass> fun1 = MyClass::new;
格式:type[] :: new
Function<Integer, Integer[]> fun = (n) -> new Integer[n];
Function<Integer, Integer[]> fun1 = Integer[]::new;
14.4 Stream API
Stream API 提供了一种高效且易于使用的处理集合数据的方式。
操作步骤:
- 创建Stream:一个数据源(如:集合、数组)获取一个流
- 中间操作:对数据源的数据进行处理,不执行终止操作前不执行
- 终止操作:一旦执行终止操作,就执行中间操作链,并产生结果。之后不能再使用
// 通过集合创建流:Collection 提供了两个获取流的方法
default Stream<E> stream() // 返回一个顺序流
default Stream<E> parallelStream() // 返回一个并行流
// 通过数组创建流: Arrays 的静态方法 stream() 可以获取数组流
static <T> Stream<T> stream(T[] array)
// 通过Stream的静态方法of()创建流
public static<T> Stream<T> of(T... values)
// 创建无限流(无限个数据对应的Stream,主要用于造数据)
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) // 迭代
public static<T> Stream<T> generate(Supplier<T> s) // 生成
// 创建无限流的例子
// 从0开始,每次加2的流;中间操作为取前十个数据,终止操作为输出
Stream<Integer> stream = Stream.iterate(0, x -> x + 2);
stream.limit(10).forEach(System.out::println);
// 生成随机数的流;中间操作为取前十个数据,终止操作为输出
Stream<Double> stream1 = Stream.generate(Math::random);
stream1.limit(10).forEach(System.out::println);
// 1-筛选与切片
filter(Predicate p) // 接收 Lambda, 从流中排除某些元素
distinct() // 通过流元素的 hashCode() 和 equals() 去重
limit(long maxSize) // 截断流,使其元素不超过给定数量
skip(long n) // 返回扔掉前 n 个元素的流。若流中元素不足 n 个,则返回个空流
// 2-映射
map(Function f) // 将函数应用到流中每个元素上
flatMap(Function f) // 将流中的每个值都换成另一个流,然后把所有流连接成一个流。该方法用于处理集合套集合的情况。例如[1,2,3,[2,3,4]]此种类型的集合,使用map处理完之后集合中有4个元素,而使用floatMap处理完之后,集合中有6个元素
// 3-排序
sorted() // 产生一个新流,按自然顺序排序
sorted(Comparator com) // 产生一个新流,按比较器顺序排序
终止操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void 。 流执行了终止操作后,将不能再次使用。
// 1-匹配与查找
allMatch(Predicate p) // 检查是否匹配所有元素
anyMatch(Predicate p) // 检查是否至少匹配一个元素
noneMatch(Predicate p) // 检查是否没有匹配所有元素
findFirst() // 返回第一个元素
findAny() // 返回当前流中的任意元素
count() // 返回流中元素总数
max(Comparator c) // 返回流中最大值
min(Comparator c) // 返回流中最小值
forEach(Consumer c) // 数据迭代
// 2-归约
reduce(T iden, BinaryOperator b) // 在初始情况为iden的情况下,使用BinaryOperator反复操作合计并累计起来,返回一个值
// 例子:计算1-10的自然数的和
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer sum1 = list.stream().reduce(0, Integer::sum);
Integer sum2 = list.stream().reduce(10, (x, y) -> x + y);
System.out.println(sum1 + " " + sum2);
// 55 65
// 3-收集
collect(Collector c) // 将Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到 List、Set、Map)。
Collectors 类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法如下:
// toList List<T> 把流中元素收集到List
List<Employee> emps= list.stream().collect(Collectors.toList());
// toSet Set<T> 把流中元素收集到Set
Set<Employee> emps= list.stream().collect(Collectors.toSet());
// counting Long 计算流中元素的个数
long count = list.stream().collect(Collectors.counting());
// summingInt Integer 对流中元素的整数属性求和
int total=list.stream().collect(Collectors.summingInt(Employee::getSalary));
// averagingInt Double 计算流中元素Integer属性的平均值
double avg = list.stream().collect(Collectors.averagingInt(Employee::getSalary));
// joining String 连接流中每个字符串
String str= list.stream().map(Employee::getName).collect(Collectors.joining());
// maxBy Optional<T> 根据比较器选择最大值
Optional<Emp>max= list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary)));
// groupingBy Map<K, List<T>> 根据某属性值对流分组,属性为K,结果为V
Map<Emp.Status, List<Emp>> map= list.stream().collect(Collectors.groupingBy(Employee::getStatus));
// partitioningBy Map<Boolean, List<T>> 根据true或false进行分区
Map<Boolean,List<Emp>> vd = list.stream().collect(Collectors.partitioningBy(Employee::getManage));
14.5 Optional类
Optional 类是一个容器类,它仅保存一个类型T的值,代表这个值存在,或者保存null,表示这个值不存在。可以避免空指针异常。
// 创建Optional类
Optional.of(T t) // 创建一个 Optional 实例,t必须非空;
Optional.empty() // 创建一个空的 Optional 实例
Optional.ofNullable(T t) // t可以为null
// 判断Optional容器是否包含对象:
boolean isPresent() : // 判断是否包含对象
void ifPresent(Consumer<? super T> consumer) // 如果有值,就执行Consumer接口的实现代码
// 获取Optional容器包含的对象:
T get() // 包含则返回该值,否则抛异常
// 通过下述方法可以保证不会返回null
T orElse(T other) // 如果有值则将其返回,否则返回指定的other对象。
T orElseGet(Supplier<? extends T> other) // 如果有值则将其返回,否则返回由Supplier接口实现提供的对象。
T orElseThrow(Supplier<? extends X> exceptionSupplier) // 如果有值则将其返回,否则抛出由Supplier接口实现提供的异常。
下面展示一些使用Optional类避免空指针异常的情况。
public void test1() {
Boy b = new Boy("张三");
Optional<Girl> opt = Optional.ofNullable(b.getGrilFriend()); // getGrilFriend可能返回null
// 如果女朋友存在就打印女朋友的信息
opt.ifPresent(System.out::println);
// 如果有女朋友就返回他的女朋友,否则只能欣赏“嫦娥”了
Girl girl = opt.orElse(new Girl("嫦娥"));
System.out.println("他的女朋友是:" + girl.getName());
}
public void test2{
Optional<Employee> opt = Optional.of(new Employee("张三", 8888));
//判断opt中员工对象是否满足条件,如果满足就保留,否则返回空
Optional<Employee> emp = opt.filter(e -> e.getSalary()>10000);
System.out.println(emp);
}