Java8新特性

111 阅读6分钟

函数式接口

函数式接口:只声明了一个抽象方法的接口,可以在函数式接口上使用 @FunctionalInterface注解,这样做可以检查它是否是一个函数式接口,同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。在 java.util.function 包下定义了 java8 丰富的函数式接口。

函数式接口的作用:可以通过 Lambda 表达式来创建实现函数式接口的类对象【若 Lambda 表达式抛出一个受检验异常,即编译时异常,那么需要在目标接口的抽象方法上进行声明】

Java常用的函数式接口

函数式接口参数类型返回类型用途
ConsumerTvoid对类型T的对象应用操作。包含方法:void accept(T t);
SupplierT返回类型为T的对象。包含方法:T get();
Function<T, R>TR对类型T的对象应用操作,并返回结果。包含方法:R apply(T t);
PredicateTboolean确定类型T的对象是否满足某约束,并返回boolean值。包含方法:boolean test(T t);
BiFunction<T, U, R>T, UR对类型T,U的对象应用操作,返回R类型的结果。包含方法:R apply(T t, R r);
UnaryOperatorTT对类型T的对象进行一元运算,并返回T类型的结果。包含方法:T apply(T t);
BinaryOperatorT, TT对类型T的对象进行二元运算,并返回T类型的结果。包含方法:T apply(T t1, T t2);
BiConsumer<T, U>T, Uvoid对类型T,U的对象应用操作。包含方法:void accept(T t, U u);

Lambda表达式

Lambda 是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。

Lambda的基本语法

形参列表 -> Lambda 体
  • -> 左侧:指定了 Lambda 表达式需要的所有参数【接口中抽象方法的形参列表】
  • -> 右侧:指定了 Lambda 体,即 Lambda 表达式要执行的功能【重写抽象方法的方法体】

Lambda的使用

// 语法格式一:无参,无返回值,Lambda体只需一条语句
Runnable r1 = () -> System.out.println("Hello Lambda");

// 语法格式二:Lambda 需要一个参数,但是没有返回值
Consumer<String> con = (str) -> System.out.println(str);

// 语法格式三:Lambda 若只需要一个参数时,参数的小括号可以省略
Consumer<String> con = str -> System.out.println(str);

// 语法格式四:Lambda 需要多个参数,多条执行语句时,并且有返回值
Comparator<Integer> com = (x, y) -> {
    System.out.println("实现函数式接口的方法");
    return Integer.compare(x, y);
}

Lambda的注意事项

  1. Lambda 表达式实际上是一条语句,这条语句会返回函数式接口的实现类对象。
  2. Lambda 表达式必须建立在函数式接口的基础之上来写的。
  3. Lambda 表达式的 -> 左边就是重写抽象方法的形参列表,右边就是重写抽象方法的方法体。
  4. Lambda 表达式可以捕获【引用】外部变量,但捕获的变量只能是最终变量;最终变量指该变量在后续代码中不会改变变量的值【final修饰的变量】。

方法引用

方法引用可以看做是 Lambda 表达式深层次的表达。方法引用就是 Lambda 表达式,也就是函数式接口的一个实例,通过方法的名字来指定一个方法,可以认为是 Lambda 表达式的一个语法糖【有些 Lambda 表达式可以用方法引用来替换,有些 Lambda 表达式不能用方法引用替换】。

方法引用的使用格式

  1. 对象::实例方法名

要求:实例接口的抽象方法的参数列表和返回类型,必须与方法引用的方法参数列表和返回类型保持一致。

  1. 类::静态方法名

要求:实例接口的抽象方法的参数列表和返回类型,必须与方法引用的方法参数列表和返回类型保持一致。

  1. 类::实例方法名

要求:实例接口的抽象方法第一个参数类型与方法引用的调用者的类型保持一致,而且实例接口的抽象方法其它参数列表和返回类型,必须与方法引用的方法参数列表和返回类型保持一致。

方法引用举例

//对象::实例方法
// Lambda实现
Consumer<String> con = str -> System.out.println(str);
// 方法引用实现
Consumer<String> con = System.out :: println;

//类::静态方法
// Lambda实现
Comparator<Integer> com = (i1, i2) -> Integer.compare(i1, 
// 方法引用实现
Comparator<Integer> com1 = Integer :: compare;

//类::实例方法
// Lambda实现
Comparator<String> com = (s1, s2) -> s1.compareTo(s2);
// 方法引用实现
Comparator<String> com1 = String :: compareTo;

Stream API

Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。

流(Stream):是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。

Stream 自己不会存储元素,Stream 不会改变源对象,他们会返回一个持有结果的新Stream。Stream 操作是延迟执行的,需要等到需要结果的时候才执行。

Stream的三个步骤

  1. 创建 Stream:一个数据源(如:集合、数组),获取一个流。
  2. 中间操作:一个中间操作链,对数据源的数据进行处理。
  3. 终止操作:一个终止操作,执行中间操作链,并产生结果。

Stream的操作的三个步骤.png

创建Stream

创建集合的Stream

List<String> strs = EmployeeData.getEmployee();
// stream(): 返回一个顺序流【顺序流,取出元素是按顺序取出的】
Stream<String> stream = strs.stream();
// parallelStream(): 返回一个并行流【并行流,取出元素是无顺序的】
Stream<String> stream = strs.parallelStream(); 

创建数组的Stream

int arr[] = {1, 2, 3, 4};
IntStream stream = Arrays.stream(arr);

User[] users = {
    new User(1001, "tom"),
    new User(1002, "marry")
};
Stream<User> stream = Arrays.stream(users); 

筛选数据

筛选数据:将Stream中的数据进行条件筛选,将不符合条件的数据删除。

//filter(Predicate p)——接收 Lambda,从流中排出某些元素
stream = stream.filter(s -> {
    return s.length() > 5;
})
    
//limit(n)——截断元素,只保留前n个元素,后面的元素删除
stream = stream.limit(3);
    
// skip(n)——跳过元素,返回一个扔掉了前 n 个元素的流,若流中元素不足n个,则返回一个空流。
stream = stream.skip(3);

//distinct()——去重元素,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
stream = stream.distinct();

映射数据

映射数据:将Stream中的每个元素进行映射,映射成另一个元素,将这些元素组成一个新的Stream。

stream = stream.map((item) -> {
    return item.toUpperCase();
})

排序数据

排序数据:将Stream中的元素按照某种排序规则进行排序处理。

// 自然排序
stream = stream.sorted();
    
// 定制排序
stream = stream.sorted((i1, i2) -> {
    return i1 - i2;
})

匹配数据

匹配数据:判断Stream中元素是否存在元素符合条件,或者所有元素符合条件。

// 检查是否匹配所有元素
boolean = stream.allMatch(item -> item.length() > 10);
    
// 检查是否至少匹配一个元素
boolean = stream.anyMatch(item -> item.length() > 10);
    
// 检查是否没有匹配的元素
boolean  = stream.noneMatch(item -> item.length() > 10);

统计数据

统计数据:将Stream中元素进行统计操作,比如求和、返回最大值、返回最小值等。

// 统计流中元素个数
long count = stream.count();
    
// 获取流中最小元素
String str = stream.min((s1, s2) -> {
    return s1.length() - s2.length();
});
    
// 获取流中最大元素
String str = stream.max((s1, s2) -> {
    return s1.length() - s2.length();
})

收集数据

收集元素:将Stream流中元素收集成一个集合或者数组

ArrayList<String> list = stream.collect(Collectors.toList());