Java8 新特性

本文详细介绍了Java 8中Lambda表达式的使用,包括无参、有参及返回值处理,函数式接口的定义和内置四大核心接口,以及Stream API的筛选、映射、排序等操作。此外,还探讨了Optional类的应用和Stream的实践案例。
摘要由CSDN通过智能技术生成

Java8 新特性

复习Java8新特性视频

1、Lambda 表达式的使用

  1. 举例:(o1,o2) -> Integer.compare(o1,o2);

  2. 格式:

    1. -> :lambda操作符 或箭头操作符
    2. 左边:lambda 形参列表(其实就是接口中的抽象方法的形参列表)
    3. 右边:lambda 体(其实就是重写的抽象方法的方法体)
  3. lambda 表达式的使用:(分为6种情况介绍)

    1. 无参,无返回值

      Runnable r1 = ()-> System.out.println("哈哈哈哈哈哈哈");
      
    2. Lambda 需要一个参数,但是没有返回值

      Consumer<String> con = (String s)-> System.out.println(s);
      con.accept("谎言和誓言的区别是什么呢?");
      
    3. 数据类型可以省略,因为可由编译器推断得出,称为 “类型推断”

      Consumer<String> con = (s)-> System.out.println(s);
      con.accept("一个是听的人当真了,一个是说的人当真了");
      
    4. Lambda 若只需要一个参数时,参数的小括号可以省略

      Consumer<String> con = s -> System.out.println(s);
      
    5. Lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值

      Comparator<Integer> com = (o1,o2) -> {
          System.out.println(o1);
          System.out.println(o2);
          return o1.compareTo(o2);
      };
      System.out.println(com.compare(12,21));
      
    6. 当 Lambda 体只有一条语句时,return 与大括号若有,都可以省略

      Comparator<Integer> com1 = (o1,o2) -> o1.compareTo(o2);
      
  4. lambda 表达式的本质:作为接口的实例

2、函数式接口

1、自定义函数式接口

1、如果一个接口中,只声明了一个抽象方法,则此就扣就称为函数式接口

2、自定义函数式接口

接口中只有一个方法就是函数式接口;

标注注解 @FunctionalInterface 是起到一个校验的作用,如果存在两个方法,注解就会报错;

不标注注解也是一个函数式接口,注解只是校验作用

@FunctionalInterface
public interface MyInterface {

 void method();

}

1、Java 内置四大核心函数式接口
函数式接口参数类型返回类型用途
Consumer
消费型接口
(你给它东西它不返回)
Tvoid对类型为 T 的对象应用操作,包含方法:
void accept(T t);
Supplier
供给型接口
(你不给它东西,它也返回)
T返回类型为 T 的对象,包含方法:
T get();
Function<T,R>
函数型接口
(传值和返回值类型不同)
TR对类型为 T 的对象应用操作,并返回结果。结果是
R类型的对象。包含方法:R apply(T t);
Predicate
断定型接口
(返回 boolean 类型数据)
Tboolean确定类型为 T 的对象是否满足某约束,并返回 boolean
值。包含方法:boolean test(T t);
3、测试
1、消费型接口
@Test
void test04(){
    // lambda 表达式
    happlyTime(500.00,money -> System.out.println("买瓶矿泉水,价格是:"+money));
}

/**
 *
 * @param money 金额
 * @param com   消费型接口,可直接使用 Lambda 表达式
 */
public void happlyTime(Double money,Consumer<Double> com){
    com.accept(money);
}
2、断定型接口
@Test
void test05(){
    List<String> list = Arrays.asList("北京", "天津", "南京", "东京", "西经", "普京");
    List<String> filterStrs = filterString(list, s -> s.contains("京"));
    System.out.println(filterStrs);
}

// 根据给定的规则,过滤集合中的字符串,规则由Predicate的实现给定
public List<String> filterString(List<String> list, Predicate<String> pre){
    ArrayList<String> arrayList = new ArrayList<>();
    for (String s : list) {
        if(pre.test(s)){
            arrayList.add(s);
        }
    }
    return arrayList;
}

3、方法引用与构造器引用

1、方法引用

使用情景:当要传递给 Lambda 体的操作,已经有实现的方法了,可以使用方法引用

方法引用,本质上就是 Lambda 表达式,而 Lambda 表达式作为函数式接口的实例。所以方法引用,也是函数式接口的实例

使用格式:类(或对象):: 方法名

具体分为如下的三种情况

对象 :: 非静态方法

使用要求:要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法的形参列表和返回值类型相同

Employee emp = new Employee(1001,"Tom",23,5600);
Supplier<String> sup2 = emp::getName;
System.out.println(sup2.get());

类 :: 静态方法

使用要求:要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法的形参列表和返回值类型相同

Comparator<Integer> com = Integer::compare;
System.out.println(com.compare(12,32));;

类 :: 非静态方法

Function<Employee,String> fun = Employee::getName;
System.out.println(fun.apply(new Employee("Tom")));;
2、构造器引用

和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致。

抽象方法的返回值类型即为构造器所属的类的类型

视频解析

对象中必须存在,Integer 的构造器,否则不能使用

测试一:

public Employee(Integer id){
 this.id = id;
}
Function<Integer,Employee> fun = Employee::new;
Employee apply = fun.apply(1001);
System.out.println(apply);

测试二:

// 因为 Employee 中存在这个有参构造
public Employee(Integer id,String name){
 this.id = id;
 this.name = name;
}
BiFunction<Integer,String,Employee> bi = Employee::new;
Employee employee = bi.apply(1001, "Tom");
System.out.println(employee);
3、数组引用

可以把数组看做是一个特殊的类,则写法与构造器引用一致。

测试:

Function<Integer,String[]> fun = String[]::new;
String[] apply = fun.apply(10);
System.out.println(Arrays.asList(apply));

4、Stream API

Stream 关注的是对数据的运算,与CPU打交道

集合关注的数据的存储,与内存打交道

Stream 自己不会存储元素

Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream

Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行

执行流程:

  1. 实例化
  2. 一系列中间操作(过滤、映射)
  3. 终止操作
1、创建方式
  1. 通过集合

    list.stream();// 顺序流
    list.parallelStream();// 并行流,几个线程同时去获取数据
    
  2. 通过数组

    // 通过 Arrays
    Arrays.stream(各种类型);
    
  3. 通过 Stream 的 of()

    // 可以调用Stream类静态方法 of(),通过显示值创建一个流。它可以接收任意数量的参数
    Stream.of(1,2,3,4,5,6,7);
    
  4. 创建无限流

    视频解析,了解即可

2、筛选与切片
方法描述
filter(Predicate p)接收 Lambda ,从流中排除某些元素
distinct()筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
limit(long maxSize)截断流,使其元素不超过给定数量
skip(long n)跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补

使用:

  1. filter

    List<Employee> data = EmployeeData.getData();
    // 过滤掉员工id小于5的数据
    data.stream().filter(e->e.getId()>5).forEach(System.out::println);
    
  2. limit

    // 只获取前三个数据
    data.stream().limit(3).forEach(System.out::println);
    
  3. skip

    // 跳过前三个数据
    data.stream().skip(3).forEach(System.out::println);
    
  4. distinct

    List<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(1);
    list.add(1);
    list.add(1);
    list.add(1);
    list.add(2);
    list.add(2);
    list.add(2);
    list.add(2);
    list.add(2);
    list.add(2);
    list.add(3);
    // 去重
    list.stream().distinct().forEach(System.out::println);
    
3、映射

Map

练习:获取员工id大于5的所有员工的姓名

List<Employee> data = EmployeeData.getData();
data.stream().filter(e->e.getId()>5)
     .map(Employee::getName)
     .forEach(System.out::println);

flatMap

接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。

@Test
void test12(){
 ArrayList<String> list = new ArrayList<>();
 list.add("aa");
 list.add("bb");
 list.add("cc");
 // 这样操作返回的是一个嵌套的stream流
 Stream<Stream<Character>> streamStream = list.stream()
         .map(LambdaTestApplicationTests::formStringToStream);
 streamStream.forEach(s->{
     s.forEach(System.out::println);
 });
 // flatMap
 list.stream().flatMap(LambdaTestApplicationTests::formStringToStream)
         .forEach(System.out::println);
}

// 数据准备
public static Stream<Character> formStringToStream(String str){
 ArrayList<Character> list = new ArrayList<>();
 for (Character c : str.toCharArray()){
     list.add(c);
 }
 return list.stream();
}
4、排序
方法描述
sorted()产生一个新流,其中按自然顺序排序
sorted(Comparator com)产生一个新流,其中按比较器顺序排序
// 自然排序
List<Integer> integers = Arrays.asList(1, 43, 556, 234, 423654, 4363);
integers.stream().sorted().forEach(System.out::println);

// 抛异常,原因 Employee 没有实现 Comparable 接口
//  List<Employee> data = EmployeeData.getData();
//  data.stream().sorted().forEach(System.out::println);


// 定制排序
List<Employee> data = EmployeeData.getData();
data.stream().sorted((e1,e2)->Integer.compare(e2.getId(), e1.getId())).forEach(System.out::println);
5、匹配与查找
方法描述
allMatch(Predicate p)检查是否匹配所有元素
anyMatch(Predicate p)检查是否至少匹配一个元素
noneMatch(Predicate p)检查是否没有匹配所有元素
findFirst()返回第一个元素
findAny()返回当前流中的任意元素
// allMatch:所有员工是否都大于18岁
List<Employee> data = EmployeeData.getData();
boolean allMatch = data.stream().allMatch(e -> e.getAge() > 18);
// anyMatch:是否存在年龄大于39的员工
boolean anyMatch = data.stream().anyMatch(e -> e.getAge() > 39);
// noneMatch(检查是否没有匹配的元素):是否存在年龄大于39的员工
boolean noneMatch = data.stream().noneMatch(e -> e.getAge() > 39);
// findFirst:取第一条元素
Optional<Employee> first = data.stream().findFirst();
// findAny:取任意元素(随机,但是stream默认是排序的,只会取到第一条数据)
Optional<Employee> any = data.stream().findAny();
System.out.println(any);
// 并行化会取一个任意元素
Optional<Employee> any1 = data.parallelStream().findAny();
System.out.println(any1);
方法描述
count返回流中元素总个数
max(Comparator c)返回流中最大值
min(Comparator c)最小值
forEach(Consumer c)内部迭代
// count:员工总数
List<Employee> data = EmployeeData.getData();
long count = data.stream().count();
System.out.println(count);
// max:年龄最大的员工
Optional<Employee> max = data.stream().max(Comparator.comparingInt(Employee::getAge));
System.out.println(max);
// min:年龄最小的员工
Optional<Employee> min = data.stream().min((e1, e2) -> Integer.compare(e1.getAge(), e2.getAge()));
System.out.println(min);
// forEach:内部迭代
data.stream().forEach(System.out::println);
// 使用集合的遍历操作
data.forEach(System.out::println);
6、归约
方法描述
reduce(T iden,BinaryOperator b)可以将流中元素反复结合起来,得到一个值。返回 T
reduce(BinaryOperator b)可以将流中元素反复结合起来,得到一个值。返回 Optional

备注:map 和 reduce 的连接通常称为 map-reduce 模式,因 Google 用它来进行网络搜索而出名

// 计算1-10的总和
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer reduce = integers.stream().reduce(0, Integer::sum);
System.out.println(reduce);
// 计算所有员工年龄的总和
List<Employee> data = EmployeeData.getData();
Optional<Integer> reduce1 = data.stream().map(Employee::getAge).reduce(Integer::sum);
System.out.println(reduce1);
7、收集
方法描述
collect(Collector c)将流转换为其他形式。接收一个 Collector 接口的实现,用于给 Stream 中元素做汇总的方法

Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到List、Set、Map)

另外,Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例见官方文档:

https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html

List<Employee> collect = data.stream().filter(e -> e.getAge() < 30).collect(Collectors.toList());
Set<Employee> set = data.stream().filter(e -> e.getAge() < 30).collect(Collectors.toSet());
// 封装map,id是key,name是value
Map<Integer, String> map = data.stream().filter(e -> e.getAge() < 30)
     .collect(Collectors.toMap(Employee::getId, Employee::getName));

5、Optional 类

  • Optional 类(java.utill.Optional) 是一个容器类,它可以保存类型 T 的值,代表这个值存在。或者仅仅保存null,表示这个值不存在。原来用 null 表示一个值不存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。
  • Optional 类的 Javadoc 描述如下:这是一个可以为null的容器对象。如果值存在则 isPresent() 方法会返回 true ,调用 get() 方法会返回该对象
  • Optional 提供很多有用的方法,这样我们就不用显式进行控制检测。

  • 创建 Optional 类对象的方法:

    • Optional.of(T t):创建一个 Optional 实例,t 必须非空;

      Employee employee = new Employee();
      employee=null;
      // of(T t):保证 t 是非空的,空的会报空指针
      Optional<Employee> optionalEmployee = Optional.of(employee);
      
    • Optional.empty():创建一个空的 Optional 实例

    • Optional.ofNullable(T t):t可以为null

      Employee employee = new Employee();
      employee=null;
      // ofNullable(T t):t 可以为空
      Optional<Employee> optionalEmployee = Optional.ofNullable(employee);
      
  • 判断 Optional 容器中是否包含对象:

    • boolean isPresent():判断是否包含对象
    • void ifPresent(Consumer<? super T> consumer):如果有值,就执行 Consumer 接口的实现代码,并且该值会作为参数传给它
  • 获取 Optional 容器的对象

    • T get():如果调用对象包含值,返回该值,否则抛异常
    • T orElse(T other):如果有值则将其返回,否则返回指定的 other 对象。
    • T orElseGet(Supplier<? extends T> other):如果有值则将其返回,否则返回由 Supplier 接口实现提供的对象。
    • T orElseThrow(Supplier<> extends X> exceptionSupplier):如果有值则将其返回,否则抛出由 Supplier 接口实现提供的异常
public String getGirlName(Boy boy){
Optional<Boy> optionalBoy = Optional.ofNullable(boy);
// 此时的boy1一定非空
Boy boy1 = optionalBoy.orElse(new Boy(new Girl("迪丽热巴")));
Girl girl = boy1.getGirl();
Optional<Girl> optionalGirl = Optional.ofNullable(girl);
// 此时的girl1一定非空
Girl girl = optionalGirl.orElse(new Girl("古力娜扎"));
return girl.getName();
}

@Test
void test(){
Boy boy = null;
String girlName = getGirlName(boy);
System.out.println(girlName);
// 结果:迪丽热巴
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值