函数式编程之Optional接口,函数式接口和方法引用

1. Optional

在编码时很容易出现空指针异常,如果过多的非空判断就会显得代码很臃肿,因此在JDK8中引入了Optional来优雅的避免出现空指针异常。

1.1 使用

Optional就好像是包装类,可以把具体数据封装Optional对象内部,然后使用Optional中封装好的方法操作封装进去的数据。

1.1.1 创建对象

  • 使用Optional.ofNullable()来创建对象:无论参数值是否为空都不会抛出异常。

         Optional<Author> author = Optional.ofNullable(getAuthor());
    
  • 使用Optional.of()来创建对象:必须要保证当前参数的对象不能为空,否则同样会抛出空指针异常。

    	Optional<Author> author1 = Optional.of(getAuthor());
    
  • 使用Optional.empty()来创建对象:会返回一个空的Optional对象。

    	Optional<Author> author2 = Optional.empty();
    

1.1.2 安全消费值

使用ifPresent()方法来对其中的值进行消费操作(建议使用lambda表达式),不为空时才会运行其中的代码。

 author.ifPresent(author3 -> System.out.println(author3));

1.1.3 安全获取值

  • 如果使用get()方法来获取,当Optional内部的数据为空时会出现异常,即存在值就返回值,不存在就抛出NoSuchElementException异常。函数式编程之Optional接口,函数式接口和方法引用_第1张图片

  • 使用orElseGet:设置一个默认值,数据为空就返回默认值,存在数据就返回数据值。
    例如:获取当前的值,如果当前不存在值,就新建一个Author对象。

    Author author4 = author.orElseGet(() -> new Author());
    
  • 使用orElseThrow:如果获取值为空,就根据传入的参数来创建异常抛出。

    author.orElseThrow(()-> new RuntimeException("抛出异常"));
    

1.1.4 过滤数据

使用filter()来对数据进行过滤。

   author.filter(author3 -> author3.getAge() > 20)
           .ifPresent(author5 -> System.out.println(author5));

1.1.5 判断

使用isPresent()来判断是否存在数据,返回boolean类型。

        boolean present = author.isPresent();

1.1.6 数据转换

使用map()来对数据进行转换,转换之后的数据也是被Optional包装好的。

author.map(author3 -> author3.getBooks())
                .ifPresent(books -> System.out.println(books));

2. 函数式接口

只有一个抽象方法的接口称为函数式接口。

常见默认方法:

  • and:用来在Predicate接口的拼接。

        /**
         * 输出年龄大于17且姓名长度大于1的作家
         */
        static void getAuthors(){
            List<Author> authors = CreateDataUtil.getAuthors();
                    authors.stream()
                            .distinct()
                            .filter(((Predicate<Author>)  author -> author.getAge() > 17).and( author -> author.getName().length() > 1))
                            .forEach(author -> System.out.println(author));
        }
    
  • and:用来在Predicate接口的判断拼接。

        /**
         * 输出年龄大于17或者姓名长度小于2的作家
         */
        static void getAuthors2(){
            List<Author> authors = CreateDataUtil.getAuthors();
            authors.stream()
                    .distinct()
                    .filter(((Predicate<Author>)  author -> author.getAge() > 17)
                            .or( author -> author.getName().length() < 2))
                    .forEach(author -> System.out.println(author));
        }
    
  • negate:用来在Predicate接口的取反判断拼接。

        /**
         * 输出年龄不大于17的作家
         */
        static void getAuthors3(){
            List<Author> authors = CreateDataUtil.getAuthors();
            authors.stream()
                    .distinct()
                    .filter(((Predicate<Author>)  author -> author.getAge() > 17)
                            .negate())
                    .forEach(author -> System.out.println(author));
        }
    

3. 方法引用

如果方法体中只有一个方法,可以使用方法引用来进一步的简化lambda表达式。

  • 基本格式:类名或者对象名::方法名
  • 推荐使用方法:在编写完lambda表达式之后,如果可以变为方法引用在idea中会有warning,然后按enter + alt转变即可。

4. Stream高级

4.1 数据类型优化

在流中使用map来计算数据时,会将基本数据类型自动装箱和拆箱,当数据量很大时,会消耗过多的内存,因此stream针对此操作进行了优化。

  • 针对基本数据类型的方法:
    1. mapToInt()
    2. mapToLong()
    3. mapToDouble()
    4. faltMapToInt()
    5. faltMapToDouble()
    6. 等…

4.2 并行流

针对大量元素,可以使用并行流来提高操作的效率。其原理就是把任务分配给多个线程去完成。

/**
 * @Description 并行流
 * @date 2022/7/7 18:46
 */
public class ParallelStream {
    public static void main(String[] args) {

        Integer[] arr = {1,2,3,4,6,7,8,9,10};
        Stream<Integer> stream = Stream.of(arr);
        stream.parallel()
                .peek(integer -> System.out.println(integer + "__" + Thread.currentThread().getName()))
                .filter(i -> i < 5)
                .reduce(Integer::sum)
                .orElseThrow(()-> new RuntimeException("抛出异常"));

    }
}

线程打印:
函数式编程之Optional接口,函数式接口和方法引用_第2张图片

你可能感兴趣的:(#,函数是编程,java)