JDK8新特性(Lambda、Stream、Optional)

2023-05-16

文章目录

    • 1、为什么使用Lambda表达式?
    • 2、Lambda表达式的使用
    • 3、函数式(functional)接口
    • 4、方法引用与构造器引用
      • 方法引用的使用
      • 构造器引用的使用
    • 5、Stream API
      • Stream 概述
      • Stream的创建
      • Stream的中间操作
        • 1. 筛选与切片
        • 2. 映射
        • 3. 排序
      • Stream的终止操作
        • 1. 匹配与查找
        • 2. 归约
        • 3. 收集
    • 6、Optional类
    • Java8思维导图


文章链接
Java语法https://blog.csdn.net/weixin_45606067/article/details/107049186
一维数组与二维数组、内存解析https://blog.csdn.net/weixin_45606067/article/details/107049178
面向对象(1/3)类和对象https://blog.csdn.net/weixin_45606067/article/details/108234276
面向对象(2/3)封装性、继承性、多态性https://blog.csdn.net/weixin_45606067/article/details/108234328
面向对象(3/3)抽象类、接口、内部类、代码块https://blog.csdn.net/weixin_45606067/article/details/108258152
异常处理待更新
多线程(1/2)https://blog.csdn.net/weixin_45606067/article/details/107067785
多线程(2/2)https://blog.csdn.net/weixin_45606067/article/details/107067857
常用类https://blog.csdn.net/weixin_45606067/article/details/108283203
枚举与注解待更新
集合(1/5)Collection、Iterator、增强forhttps://blog.csdn.net/weixin_45606067/article/details/107046876
集合(2/5)List、ArrayList、LinkedList、Vector的底层源码https://blog.csdn.net/weixin_45606067/article/details/107069742
集合(3/5)set、HashSet、LinkedHashSet、TreeSet的底层源码
集合(4/5)Map、HashMap底层原理分析https://blog.csdn.net/weixin_45606067/article/details/107042949
集合(5/5)LinkHashMap、TreeMap、Properties、Collections工具类https://blog.csdn.net/weixin_45606067/article/details/107069691
泛型与Filehttps://blog.csdn.net/weixin_45606067/article/details/107124099
IO流与网络编程https://blog.csdn.net/weixin_45606067/article/details/107143670
反射机制待更新
Java8新特性https://blog.csdn.net/weixin_45606067/article/details/107280823
Java9/10/11新特性待更新

1、为什么使用Lambda表达式?

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

2、Lambda表达式的使用

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

2.格式:

  • ->:lambda操作符 或 箭头操作符。
  • ->左边:lambda形参列表 (其实就是接口中的抽象方法的形参列表)。
  • ->右边:lambda体 (其实就是重写的抽象方法的方法体)。

3.Lambda表达式的使用:(分为6种情况介绍)

总结
->左边:lambda形参列表的参数类型可以省略(类型推断);如果lambda形参列表只有一个参数,其一对()也可以省略。
->右边:lambda体应该使用一对{}包裹;如果lambda体只有一条执行语句(可能是return语句),省略这一对{}和return关键字。

4.Lambda表达式的本质:作为函数式接口的实例。

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

我们可以在一个接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口。

/**
 * 自定义函数式接口
 * 
 */
@FunctionalInterface
public interface MyInterface {

    void method1();
}

6.所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。

代码实现:

//语法格式一:无参,无返回值
@Test
public void test1(){
  Runnable r1 = new Runnable() {
    @Override
    public void run() {
      System.out.println("我爱北京天安门");
    }
  };
  r1.run();

  System.out.println("***********************");

  Runnable r2 = () -> {
    System.out.println("我爱北京故宫");
  };
  r2.run();
}

//语法格式二:Lambda 需要一个参数,但是没有返回值。
@Test
public void test2(){
  Consumer<String> con = new Consumer<String>() {
    @Override
    public void accept(String s) {
      System.out.println(s);
    }
  };
  con.accept("谎言和誓言的区别是什么?");

  System.out.println("*******************");

  Consumer<String> con1 = (String s) -> {
    System.out.println(s);
  };
  con1.accept("一个是听得人当真了,一个是说的人当真了");
}

//语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断”
@Test
public void test3(){

  Consumer<String> con1 = (String s) -> {
    System.out.println(s);
  };
  con1.accept("一个是听得人当真了,一个是说的人当真了");

  System.out.println("*******************");

  Consumer<String> con2 = (s) -> {
    System.out.println(s);
  };
  con2.accept("一个是听得人当真了,一个是说的人当真了");

  System.out.println("*******************");

  Consumer<String> con3 = s -> System.out.println(s);
  con3.accept("一个是听得人当真了,一个是说的人当真了");
}

//语法格式四:Lambda 若只需要一个参数时,参数的小括号可以省略
@Test
public void test4(){
  Consumer<String> con1 = (s) -> {
    System.out.println(s);
  };
  con1.accept("一个是听得人当真了,一个是说的人当真了");

  System.out.println("*******************");

  Consumer<String> con2 = s -> {
    System.out.println(s);
  };
  con2.accept("一个是听得人当真了,一个是说的人当真了");
}

//语法格式五:Lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值
@Test
public void test5(){

  Comparator<Integer> com1 = new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
      System.out.println(o1);
      System.out.println(o2);
      return o1.compareTo(o2);
    }
  };
  System.out.println(com1.compare(12,21));

  System.out.println("*****************************");
  Comparator<Integer> com2 = (o1,o2) -> {
    System.out.println(o1);
    System.out.println(o2);
    return o1.compareTo(o2);
  };
  System.out.println(com2.compare(12,6));
}

//语法格式六:当 Lambda 体只有一条语句时,return 与大括号若有,都可以省略
@Test
public void test6(){
  Comparator<Integer> com1 = (o1,o2) -> {
    return o1.compareTo(o2);
  };
  System.out.println(com1.compare(12,6));

  System.out.println("*****************************");

  Comparator<Integer> com2 = (o1, o2) -> o1.compareTo(o2);
  System.out.println(com2.compare(12,21));
}

3、函数式(functional)接口

1.定义:只包含一个抽象方法的接口,称为 函数式接口。

2.在java.util.function包下定义了Java 8 的丰富的函数式接口。

3.Java内置的四大核心函数式接口

Consumer<T>消费性接口  	 void accept(T t)
Supplier<T>供给型接口   	 T get()
Function<T,R>函数型接口   R apply(T t)
Predicate<T>断定型接口 	 boolean test(T t)
@Test
public void test1(){
  happyTime(500, new Consumer<Double>() {
    @Override
    public void accept(Double aDouble) {
      System.out.println("学习太累了,去天上人间买了瓶矿泉水,价格为:" + aDouble);
    }
  });

  System.out.println("********************");

  happyTime(400,money -> System.out.println("学习太累了,去天上人间喝了口水,价格为:" + money));
}

public void happyTime(double money, Consumer<Double> con){
  con.accept(money);
}

@Test
public void test2(){
  List<String> list = Arrays.asList("北京","南京","天津","东京","西京","普京");

  List<String> filterStrs = filterString(list, new Predicate<String>() {
    @Override
    public boolean test(String s) {
      return s.contains("京");
    }
  });
  System.out.println(filterStrs);

  System.out.println("*********************");

  List<String> filterStrs1 = filterString(list, s -> s.contains("京"));
  System.out.println(filterStrs1);
}

//根据给定的规则,过滤集合中的字符串。此规则由Predicate的方法决定
public List<String> filterString(List<String> list, Predicate<String> pre){

  ArrayList<String> filterList = new ArrayList<>();

  for(String s : list){
    if(pre.test(s)){
      filterList.add(s);
    }
  }
  return filterList;
}

其他接口
在这里插入图片描述

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

方法引用的使用

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

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

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

  4. 具体分为如下的三种情况:

  • 情况1 对象 :: 非静态方法
  • 情况2 类 :: 静态方法
  • 情况3 类 :: 非静态方法
  1. 方法引用使用的要求:要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法的形参列表和返回值类型相同!(针对于情况1和情况2)
// 情况一:对象 :: 实例方法
//Consumer中的void accept(T t)
//PrintStream中的void println(T t)
@Test
public void test1() {
  Consumer<String> con1 = str -> System.out.println(str);
  con1.accept("北京");

  System.out.println("*******************");
  PrintStream ps = System.out;
  Consumer<String> con2 = ps::println;
  con2.accept("beijing");
}

//Supplier中的T get()
//Employee中的String getName()
@Test
public void test2() {
  Employee emp = new Employee(1001,"Tom",23,5600);

  Supplier<String> sup1 = () -> emp.getName();
  System.out.println(sup1.get());

  System.out.println("*******************");
  Supplier<String> sup2 = emp::getName;
  System.out.println(sup2.get());
}

// 情况二:类 :: 静态方法
//Comparator中的int compare(T t1,T t2)
//Integer中的int compare(T t1,T t2)
@Test
public void test3() {
  Comparator<Integer> com1 = (t1,t2) -> Integer.compare(t1,t2);
  System.out.println(com1.compare(12,21));

  System.out.println("*******************");

  Comparator<Integer> com2 = Integer::compare;
  System.out.println(com2.compare(12,3));
}

//Function中的R apply(T t)
//Math中的Long round(Double d)
@Test
public void test4() {
  Function<Double,Long> func = new Function<Double, Long>() {
    @Override
    public Long apply(Double d) {
      return Math.round(d);
    }
  };

  System.out.println("*******************");

  Function<Double,Long> func1 = d -> Math.round(d);
  System.out.println(func1.apply(12.3));

  System.out.println("*******************");

  Function<Double,Long> func2 = Math::round;
  System.out.println(func2.apply(12.6));
}

// 情况三:类 :: 实例方法  (有难度)
// Comparator中的int comapre(T t1,T t2)
// String中的int t1.compareTo(t2)
@Test
public void test5() {
  Comparator<String> com1 = (s1,s2) -> s1.compareTo(s2);
  System.out.println(com1.compare("abc","abd"));

  System.out.println("*******************");

  Comparator<String> com2 = String :: compareTo;
  System.out.println(com2.compare("abd","abm"));
}

//BiPredicate中的boolean test(T t1, T t2);
//String中的boolean t1.equals(t2)
@Test
public void test6() {
  BiPredicate<String,String> pre1 = (s1,s2) -> s1.equals(s2);
  System.out.println(pre1.test("abc","abc"));

  System.out.println("*******************");
  BiPredicate<String,String> pre2 = String :: equals;
  System.out.println(pre2.test("abc","abd"));
}

// Function中的R apply(T t)
// Employee中的String getName();
@Test
public void test7() {
  Employee employee = new Employee(1001, "Jerry", 23, 6000);

  Function<Employee,String> func1 = e -> e.getName();
  System.out.println(func1.apply(employee));

  System.out.println("*******************");

  Function<Employee,String> func2 = Employee::getName;
  System.out.println(func2.apply(employee));
}

构造器引用的使用

  1. 构造器引用
    和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致。
    抽象方法的返回值类型即为构造器所属的类的类型。

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

//构造器引用
//Supplier中的T get()
//Employee的空参构造器:Employee()
@Test
public void test1(){
  Supplier<Employee> sup = new Supplier<Employee>() {
    @Override
    public Employee get() {
      return new Employee();
    }
  };
  System.out.println("*******************");

  Supplier<Employee>  sup1 = () -> new Employee();
  System.out.println(sup1.get());

  System.out.println("*******************");

  Supplier<Employee>  sup2 = Employee :: new;
  System.out.println(sup2.get());
}

//Function中的R apply(T t)
@Test
public void test2(){
  Function<Integer,Employee> func1 = id -> new Employee(id);
  Employee employee = func1.apply(1001);
  System.out.println(employee);

  System.out.println("*******************");

  Function<Integer,Employee> func2 = Employee :: new;
  Employee employee1 = func2.apply(1002);
  System.out.println(employee1);
}

//BiFunction中的R apply(T t,U u)
@Test
public void test3(){
  BiFunction<Integer,String,Employee> func1 = (id,name) -> new Employee(id,name);
  System.out.println(func1.apply(1001,"Tom"));

  System.out.println("*******************");

  BiFunction<Integer,String,Employee> func2 = Employee :: new;
  System.out.println(func2.apply(1002,"Tom"));
}

//数组引用
//Function中的R apply(T t)
@Test
public void test4(){
  Function<Integer,String[]> func1 = length -> new String[length];
  String[] arr1 = func1.apply(5);
  System.out.println(Arrays.toString(arr1));

  System.out.println("*******************");

  Function<Integer,String[]> func2 = String[] :: new;
  String[] arr2 = func2.apply(10);
  System.out.println(Arrays.toString(arr2));
}

5、Stream API

Stream 概述

Java 8 是一个非常成功的版本,这个版本新增的Stream,配合同版本出现的 Lambda ,给我们操作集合(Collection)提供了极大的便利。

那么什么是 Stream

Stream将要处理的元素集合看作一种流,在流的过程中,借助Stream API对流中的元素进行操作,比如:筛选、排序、聚合等。

Stream可以由数组或集合创建,对流的操作分为两种:

  1. 中间操作,每次返回一个新的流,可以有多个。
  2. 终端操作,每个流只能进行一次终端操作,终端操作结束后流无法再次使用。终端操作会产生一个新的集合或值。

另外,Stream有几个特性:

  • stream不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果。
  • stream不会改变数据源,通常情况下会产生一个新的集合或一个值。
  • stream具有延迟执行特性,只有调用终端操作时,中间操作才会执行。

在这里插入图片描述

Stream的创建

  1. 通过Collection 系列集合提供的 stream()parallelStream()
List<String> list = Arrays.asList("a", "b", "c");
// 创建一个顺序流
Stream<String> stream = list.stream();
// 创建一个并行流
Stream<String> parallelStream = list.parallelStream();
  1. 通过Arrays中的静态方法 stream() 获取数组流
int[] array={1,3,5,6,8};
IntStream stream = Arrays.stream(array);
  1. 通过 Stream 的静态方法:of()iterate()generate()
Stream<String> stream2 = Stream.of("aaa", "bbb", "ccc");

Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2);
stream3.limit(10).forEach(System.out::println);

Stream<Double> stream4 = Stream.generate(() -> Math.random());
stream4.limit(5).forEach(System.out::println);

streamparallelStream的简单区分: stream是顺序流,由主线程按顺序对流执行操作,而parallelStream是并行流,内部以多线程并行执行的方式对流进行操作,但前提是流中的数据处理没有顺序要求。例如筛选集合中的奇数,两者的处理不同之处:

在这里插入图片描述

Stream的中间操作

1. 筛选与切片

  • filter:接收Lambda,从流中排除掉某些元素
  • limit:截断流,使其元素不超过给定数量
  • skip(n):跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n)互补。
  • distinct:筛选,通过流所生成元素的hashCode() 和 equals() 去除重复元素
List<Employee> employees = Arrays.asList(
        new Employee("张三",28,9999.99),
        new Employee("李四",18,8888.99),
        new Employee("王五",38,7777.99),
        new Employee("孙刘",50,5555.99),
        new Employee("田七",8,3333.99),
        new Employee("李四",8,8888.99)
);

// 内部迭代:迭代操作由Stream API完成
@Test
public void test1() {
    //中间操作:不会执行任何操作
    Stream<Employee> stream = employees.stream()
            .filter((e) -> e.getAge() > 35);
    //终止操作:一次性执行全部内容,即 惰性求值
    stream.forEach(System.out::println);
}

// 外部迭代
@Test
public void test2() {
    Iterator<Employee> iterator = employees.iterator();
    while(iterator.hasNext()) {
        System.out.println(iterator.next());
    }
}

@Test
public void test3() {
    employees.stream()
            .filter((e) -> e.getSalary() > 5000)
            .limit(2)
            .forEach(System.out::println);
}

@Test
public void test4() {
    employees.stream()
            .filter((e) -> e.getSalary() > 5000)
            .skip(2)
            .forEach(System.out::println);
}

@Test
public void test5() {
    employees.stream()
            .filter((e) -> e.getSalary() > 5000)
            .distinct()
            .forEach(System.out::println);
}

2. 映射

  • map:接收Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
  • flatMap:接收一个函数作为参数,将流中的每个值都转换成另一个流,然后把所有流连接成一个流。
List<Employee> employees = Arrays.asList(
        new Employee("张三",28,9999.99),
        new Employee("李四",18,8888.99),
        new Employee("王五",38,7777.99),
        new Employee("孙刘",50,5555.99),
        new Employee("田七",8,3333.99),
        new Employee("李四",8,8888.99)
);

@Test
public void test6() {
    List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
    // 将字母转化为大写
    list.stream()
            .map((str) -> str.toUpperCase())
            .forEach(System.out::println);

    System.out.println("--------------");

    // 获取员工姓名
    employees.stream()
            .map(Employee::getName)
            .forEach(System.out::println);

    System.out.println("--------------");

    // 获取字符串中每一个字符
    Stream<Stream<Character>> stream = list.stream()
            .map(TestStreamAPI2::filterCharacher);
    stream.forEach((sm) -> {
        sm.forEach(System.out::println);
    });

    System.out.println("--------------");

    Stream<Character> stream1 = list.stream()
            .flatMap(TestStreamAPI2::filterCharacher);
    stream1.forEach(System.out::println);
}

// 将字符串中的多个字符构成的集合转换为对应的Stream的实例
public static Stream<Character> filterCharacher(String str) {
    List<Character> list = new ArrayList<>();
    for (Character ch : str.toCharArray()) {
        list.add(ch);
    }
    return list.stream();
}

3. 排序

  • sorted():自然排序
  • sorted(Comparator com):定制排序
@Test
public void test7() {
    List<String> list = Arrays.asList("ccc","aaa", "ddd", "eee", "bbb");
    list.stream()
            .sorted()
            .forEach(System.out::println);

    System.out.println("------------");

    employees.stream()
            .sorted((e1, e2) -> {
                if (e1.getAge().equals(e2.getAge())) {
                    return e1.getName().compareTo(e2.getName());
                } else {
                    return e1.getAge().compareTo(e2.getAge());
                }
            }).forEach(System.out::println);
}

Stream的终止操作

1. 匹配与查找

  • allMatch:检查是否匹配所有元素
  • anyMatch:检查是否至少匹配一个元素
  • noneMatch:检查是否没有匹配所有元素
  • findFirst:返回第一个元素
  • findAny:返回当前流中的任意元素
  • count:返回流中元素的总个数
  • max:返回流中最大值
  • min:返回流中最小值
@Test
public void test1() {
    boolean b1 = employees.stream()
            .allMatch((e) -> e.getStatus().equals(Status.BUSY));
    System.out.println(b1);

    System.out.println("--------");

    boolean b2 = employees.stream()
            .anyMatch((e) -> e.getStatus().equals(Status.BUSY));
    System.out.println(b2);

    System.out.println("--------");

    boolean b3 = employees.stream()
            .noneMatch((e) -> e.getStatus().equals(Status.BUSY));
    System.out.println(b3);

    System.out.println("--------");

    Optional<Employee> op = employees.stream()
            .sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
            .findFirst();
    System.out.println(op.get());

    System.out.println("--------");

    Optional<Employee> op2 = employees.parallelStream()
            .filter((e) -> e.getStatus().equals(Status.FREE))
            .findAny();
    System.out.println(op2.get());
}

@Test
public void test2() {
    long count = employees.stream()
            .count();
    System.out.println(count);

    Optional<Employee> max = employees.stream()
            .max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
    System.out.println(max);

    Optional<Double> min = employees.stream()
            .map(Employee::getSalary)
            .min(Double::compare);
    System.out.println(min);
}

2. 归约

  • reduce(T identity,BinaryOperator) / reduce(BinarOperator):可以将流中元素反复结合起来,得到一个值
@Test
public void test3(){
  //reduce(T identity, BinaryOperator)——可以将流中元素反复结合起来,得到一个值。返回 T
  //练习1:计算1-10的自然数的和
  List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
  Integer sum = list.stream().reduce(0, Integer::sum);
  System.out.println(sum);

  //reduce(BinaryOperator) ——可以将流中元素反复结合起来,得到一个值。返回 Optional<T>
  //练习2:计算公司所有员工工资的总和
  Optional<Double> op = employees.stream()
          .map(Employee::getSalary)
          .reduce(Double::sum);
  System.out.println(op);
}

3. 收集

  • collect(Collector c)——将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
// 查找员工姓名,结果返回为一个List、Set或Collection
@Test
public void test4() {
    List<String> list = employees.stream()
            .map(Employee::getName)
            .collect(Collectors.toList());
    list.forEach(System.out::println);

    System.out.println("------------");

    Set<String> set = employees.stream()
            .map(Employee::getName)
            .collect(Collectors.toSet());
    set.forEach(System.out::println);

    System.out.println("------------");

    HashSet<String> hs = employees.stream()
            .map(Employee::getName)
            .collect(Collectors.toCollection(HashSet::new));
    hs.forEach(System.out::println);
}
  • 计数:counting
  • 平均值:averagingIntaveragingLongaveragingDouble
  • 最值:maxByminBy
  • 求和:summingIntsummingLongsummingDouble
  • 统计以上所有:summarizingIntsummarizingLongsummarizingDouble
@Test
public void test5() {
    // 总数
    Long count = employees.stream()
            .collect(Collectors.counting());
    System.out.println(count);

    //平均值
    Double avg = employees.stream()
            .collect(Collectors.averagingDouble(Employee::getSalary));
    System.out.println(avg);

    //总和
    DoubleSummaryStatistics sum = employees.stream()
            .collect(Collectors.summarizingDouble(Employee::getSalary));
    System.out.println(sum);

    // 最大值
    Optional<Employee> max = employees.stream()
            .collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
    System.out.println(max);

    // 最小值
    Optional<Double> min = employees.stream()
            .map(Employee::getSalary)
            .collect(Collectors.minBy(Double::compare));
    System.out.println(min);
}
  • 分组:将集合分为多个Map
  • 分区:将stream按条件分为两个Map
@Test
public void test6() {
    // 分组
    Map<Status, List<Employee>> map = employees.stream()
            .collect(Collectors.groupingBy(Employee::getStatus));
    System.out.println(map);
}

@Test
public void test7() {
    // 多级分组
    Map<Status, Map<String, List<Employee>>> map = employees.stream()
            .collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {
                if (e.getAge() <= 35) {
                    return "青年";
                } else if (e.getAge() <= 50) {
                    return "中年";
                } else {
                    return "老年";
                }
            })));
    System.out.println(map);
}
@Test
public void test8() {
    // 分区
    Map<Boolean, List<Employee>> map = employees.stream()
            .collect(Collectors.partitioningBy((e) -> e.getSalary() > 8000));
    System.out.println(map);
}
  • 连接(joining):可以将stream中的元素用特定的连接符(没有的话,则直接连接)连接成一个字符串
@Test
public void test10() {
    String str = employees.stream()
            .map(Employee::getName)
            .collect(Collectors.joining(","));
    System.out.println(str);
}
  • 其他获取最大值、平均值、最小值、总数的方式
@Test
public void test9() {
    DoubleSummaryStatistics dss = employees.stream()
            .collect(Collectors.summarizingDouble(Employee::getSalary));
    System.out.println(dss.getAverage());
    System.out.println(dss.getCount());
    System.out.println(dss.getMin());
}

以上应用到的 Employee 实体类

public class Employee {

    private String name;
    private Integer age;
    private double salary;
    private Status status;

    public enum Status{
        FREE,
        BUSY,
        VOCATION;
    }
}

6、Optional类

Optional 容器类的常用方法:

  • Optional.of(T t) : 创建一个 Optional 实例,t必须非空;
  • Optional.empty() : 创建一个空的 Optional 实例
  • Optional.ofNullable(T t):若 t 不为null,创建Optional 实例,否则创建空实例
  • isPresent():判断是否包含值
  • orElse(T t):判断是否包含值
  • orElseGet(Supplier s):如果调用对象包含值,返回值,否则返回s获取的值map(Function f):如果有值对其处理,并返回处理后的
  • Optional,否则返回Optional.empty()
  • flatMap(Function mapper):与map类似,要求返回值必须是Optional
/**
 * Optional类:为了在程序中避免出现空指针异常而创建的。
 *
 * 常用的方法:ofNullable(T t)
 *            orElse(T t)
 */
public class OptionalTest {
    /*
    Optional.of(T t) : 创建一个 Optional 实例,t必须非空;
    Optional.empty() : 创建一个空的 Optional 实例
    Optional.ofNullable(T t):t可以为null
     */
    @Test
    public void test1(){
        Girl girl = new Girl();
//        girl = null;
        // of(T t):保证t是非空的
        Optional<Girl> optionalGirl = Optional.of(girl);
    }

    @Test
    public void test2(){
        Girl girl = new Girl();
//        girl = null;
        // ofNullable(T t):t可以为null
        Optional<Girl> optionalGirl = Optional.ofNullable(girl);
        System.out.println(optionalGirl);
        
        // orElse(T t1):如果单前的Optional内部封装的t是非空的,则返回内部的t.
        // 如果内部的t是空的,则返回orElse()方法中的参数t1.
        Girl girl1 = optionalGirl.orElse(new Girl("赵丽颖"));
        System.out.println(girl1);
    }

    public String getGirlName(Boy boy){
        return boy.getGirl().getName();
    }

    @Test
    public void test3(){
        Boy boy = new Boy();
        boy = null;
        String girlName = getGirlName(boy);
        System.out.println(girlName);
    }
    //优化以后的getGirlName():
    public String getGirlName1(Boy boy){
        if(boy != null){
            Girl girl = boy.getGirl();
            if(girl != null){
                return girl.getName();
            }
        }
        return null;
    }

    @Test
    public void test4(){
        Boy boy = new Boy();
        boy = null;
        String girlName = getGirlName1(boy);
        System.out.println(girlName);
    }

    //使用Optional类的getGirlName():
    public String getGirlName2(Boy boy){

        Optional<Boy> boyOptional = Optional.ofNullable(boy);
        //此时的boy1一定非空
        Boy boy1 = boyOptional.orElse(new Boy(new Girl("迪丽热巴")));

        Girl girl = boy1.getGirl();

        Optional<Girl> girlOptional = Optional.ofNullable(girl);
        //girl1一定非空
        Girl girl1 = girlOptional.orElse(new Girl("古力娜扎"));

        return girl1.getName();
    }

    @Test
    public void test5(){
        Boy boy = null;
        boy = new Boy();
        boy = new Boy(new Girl("苍老师"));
        String girlName = getGirlName2(boy);
        System.out.println(girlName);
    }
}
public class Boy {
    private Girl girl;

    @Override
    public String toString() {
        return "Boy{" +
                "girl=" + girl +
                '}';
    }

    public Girl getGirl() {
        return girl;
    }

    public void setGirl(Girl girl) {
        this.girl = girl;
    }

    public Boy() {
    }

    public Boy(Girl girl) {
        this.girl = girl;
    }
}
public class Girl {
    private String name;

    @Override
    public String toString() {
        return "Girl{" +
                "name='" + name + '\'' +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Girl() {
    }

    public Girl(String name) {
        this.name = name;
    }
}

Java8思维导图

在这里插入图片描述


如果有收获!!! 希望老铁们来个三连,点赞、收藏、转发
创作不易,别忘点个赞,可以让更多的人看到这篇文章,顺便鼓励我写出更好的博客
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JDK8新特性(Lambda、Stream、Optional) 的相关文章

随机推荐

  • python: image.paste函数的理解

    目录 image paste函数 举例说明 xff1a image paste函数 python中PIL库中的paste函数的作用为将一张图片覆盖到另一张图片的指定位置去 函数的声明如下 xff1a def paste self im bo
  • JDBC(3)实现通用的增删改查方法

    编写通用的增删改查方法 不明白的可以看代码中注释 xff0c 写的很详细 1 通用的增删改 span class token comment 通用的更新数据库的方法 xff1a insert update delete 语句时 span s
  • JDBC(4)DBCP数据源 | C3P0 数据源

    1 DBCP 数据源 DBCP 是 Apache 软件基金组织下的开源连接池实现 xff0c 该连接池依赖该组织下的另一个开源系统 xff1a Common pool 如需使用该连接池实现 xff0c 应在系统中增加如下两个 jar 文件
  • JDBC(5)DBUtils类详解

    1 DbUtils 类 DbUtils xff1a 提供如关闭连接 装载 JDBC 驱动程序等常规工作的工具类 xff0c 里面的所有方法都是静态的 主要方法如下 xff1a public static void close throws
  • 验证码实现 - html页面版

    前言 图片验证码是我们日常经常用到的 xff0c 本文将介绍如何实现以及其原理 xff0c 并没有过多注重css样式 xff0c 单纯实现验证码功能 如果对滑块验证码感兴趣的可以看这篇文章 xff1a 滑块验证码实现及原理 如果对验证码实现
  • 验证码实现 - 工具类调用版

    前言 图片验证码是我们日常经常用到的 xff0c 本文将介绍如何实现以及其原理 xff0c 以注册页面为例实现功能 如果对滑块验证码感兴趣的可以看这篇文章 xff1a 滑块验证码实现及原理 如果对验证码实现感兴趣的可以看这篇文章 xff1a
  • 滑块验证码实现

    前言 滑块验证码也是生活中常见的 xff0c 本文会介绍如何实现以及原理 如果对验证码实现感兴趣的可以看这篇文章 xff1a 验证码实现 html页面版 如果对验证码实现感兴趣的可以看这篇文章 xff1a 验证码实现 工具类调用版 代码实现
  • MySQL(1)的使用 | SQL

    目录 MySQL 的使用1 启动和停止服务方式一 xff1a 图形化方式方式二 xff1a 命令行 2 客户端登录方式一 xff1a MySQL 自带客户端方式二 xff1a 命令行方式三 xff1a 可视化工具 SQL1 SQL 的语言规
  • MySQL(2)DDL详解

    一 DDL 1 1 操作 Database 注意 xff1a database 不能改名 一些可视化工具可以改名 xff0c 它是建新库 xff0c 把所有表复制到新库 xff0c 再删旧库完成的 1 创建数据库 create databa
  • MySQL(4)运算符 | 关联查询详解

    一 MySQL 的运算符 xff08 1 xff09 算术运算符 xff1a 43 xff08 除也可以写成 div xff08 取模可以写成 mod xff09 xff08 2 xff09 比较运算符 xff1a 61 gt gt 61
  • MySQL(5)条件查询 | 单行函数 | 事务详解

    一 select 的 的 5 个子句 1 where 条件查询 从原表中的记录中进行筛选 2 group by 分组查询 很多情况下 xff0c 用户都需要进行一些汇总操作 xff0c 比如统计整个公司的人数或者统计每一个部门的人数等 聚合
  • python:tqdm——进度条显示操作

    在代码执行过程中 xff0c 如果想要看到代码的实时运行进度 xff0c 可以使用tqdm库来进行进度条可视化 tqdm的安装 xff1a pip install tqdm 举一个常用参数的使用例子 xff1a from tqdm impo
  • MySQL(3)DML详解

    一 DML 数据操纵语言 xff08 DML xff09 DML 用于插入 修改 删除数据记录 xff0c 包括如下 SQL 语句 xff1a INSERT xff1a 添加数据到数据库中 UPDATE xff1a 修改数据库中的数据 DE
  • 泛型的使用与通配符

    文章目录 泛型的使用1 jdk1 5新特性泛型2 为什么要使用泛型 xff1f 3 在集合中使用泛型 自定义泛型结构 xff1a 泛型类 xff0c 泛型接口 xff1b 泛型方法泛型类被某个类继承自定义泛型的注意点泛型方法 泛型在继承方面
  • IO流详解

    文章目录 File类1 File类的使用2 如何创建File类的实例3 常用方法 IO流1 概述2 节点流 字符流FileReader读入数据的操作FileWriter写出数据的操作使用FileReader和FileWriter实现文本文件
  • TCP的三次握手和四次挥手详解

    1 三次握手 三次握手 xff08 Three way Handshake xff09 其实就是指建立一个TCP连接时 xff0c 需要客户端和服务器总共发送3个包 进行三次握手的主要作用就是为了确认双方的接收能力和发送能力是否正常 指定自
  • 多线程 / 高并发 高频考点

    1 stop 和 suspend 方法为何不推荐使用 xff1f 反对使用stop xff0c 是因为它不安全 它会解除由线程获取的所有锁定 xff0c 而且如果对象处于一种不连贯状态 xff0c 那么其他线程能在那种状态下检查和修改它们
  • Synchronized、Lock、ReentrantLock详解

    一 synchronized的缺陷 synchronized是java中的一个关键字 xff0c 也就是说是java语言内置的特性 那么为什么会出现Lock呢 xff1f 如果一个代码块被synchronized修饰了 xff0c 当一个线
  • HashMap在JDK1.7和1.8中的实现

    一 初窥HashMap HashMap是应用更广泛的哈希表实现 xff0c 而且大部分情况下 xff0c 都能在常数时间性能的情况下进行put和get操作 要掌握HashMap xff0c 主要从如下几点来把握 xff1a jdk1 7中底
  • JDK8新特性(Lambda、Stream、Optional)

    文章目录 1 为什么使用Lambda表达式 xff1f 2 Lambda表达式的使用3 函数式 xff08 functional xff09 接口4 方法引用与构造器引用方法引用的使用构造器引用的使用 5 Stream APIStream