java8新特性——Function&Stream&Optional

java8新特性——Function&Stream&Optional

函数式编程

简介

函数式编程,或称函数程序设计、泛函编程,是一种编程范型,它将电脑运算视为函数运算,并且避免使用程式状态以及可变物件。这意味着一个函数,既可以作为其它函数的输入参数值,也可以从函数中返回值,被修改或者被分配给一个变量。

比起指令式编程,函数式编程更加强调程序执行的结果而非执行的过程,倡导利用若干简单的执行单元让计算结果不断渐进,逐层推导复杂的运算,而不是设计一个复杂的执行过程。

在java中,函数式编程有以下特征:

  • 接口中有且只有一个抽象方法;
  • 接口被@FunctionalInterface注解修饰;
  • 使用Lambda表达式作为入参和返回值;

常用的函数式接口

  1. BinaryOperator extends BiFunction<T,T,T>

    表示两个相同类型的操作数进行运算,产生与操作数相同类型的结果。

    BinaryOperator<Integer> addition = (x, y) -> x + y;
    BinaryOperator<Integer> subtraction = (x, y) -> x - y;
    BinaryOperator<Integer> multiplication = (x, y) -> x * y;
    BinaryOperator<Double> division = (x, y) -> x / y;
    
    System.out.println("10 + 5 = " + addition.apply(10, 5));
    System.out.println("10 - 5 = " + subtraction.apply(10, 5));
    System.out.println("10 * 5 = " + multiplication.apply(10, 5));
    System.out.println("10.0 / 2.0 = " + division.apply(10.0, 2.0));
    
    // 执行结果
    10 + 5 = 15
    10 - 5 = 5
    10 * 5 = 50
    10.0 / 2.0 = 5.0
    
    List<User> list0 = Arrays.asList(
            User.builder().name("Emma Watson").age(18).sex("女").build(),
            User.builder().name("Angelina Jolie").age(19).sex("女").build(),
            User.builder().name("Emilia Clarke").age(20).sex("女").build(),
            User.builder().name("Anna Kendrick").age(21).sex("女").build(),
            User.builder().name("Tony").age(21).sex("男").build(),
            User.builder().name("Tom").age(22).sex("男").build(),
            User.builder().name("Jerry").age(23).sex("男").build(),
            User.builder().name("Marks").age(24).sex("男").build()
    );
    Comparator<User> comparator = Comparator.comparing(User::getAge);
    Map<String, Optional<User>> map = list0.stream().collect(Collectors.groupingBy(User::getSex,
            Collectors.reducing(BinaryOperator.maxBy(comparator))));
    map.entrySet().forEach(entry -> {
        System.out.printf("key:  %s + value: %s \n", entry.getKey(), entry.getValue());
    });
    
    // 执行结果
    key:+ value: Optional[User(name=Anna Kendrick, sex=, age=21)] 
    key:+ value: Optional[User(name=Marks, sex=, age=24)] 
    
  2. Consumer

    消费型接口,且无返回值。

    Consumer consumer1 = s -> {
        s += " is good day!";
        System.out.println(s);
    };
    
    Consumer consumer2 = s -> {
        s = "Yesterday is a history, tomorrow is a mystery, but today is a gift, that is why it is called Present.";
        System.out.println(s);
    };
    
    consumer1.andThen(consumer2).accept("Today");
    
    // 执行结果
    Today is good day!
    Yesterday is a history, tomorrow is a mystery, but today is a gift, that is why it is called Present.
    
  3. Function<T, R>

    函数型接口,接收一个参数,返回一个结果。

    andThen()先执行function1.apply(),再执行function2.apply();compose()则相反。

    Function function1 = a -> new StringBuffer(a.toString()).reverse().toString();
    Function function2 = a -> a.toString().toUpperCase();
    String str1 = (String) function1.andThen(function2).apply("hello world");
    String str2 = (String) function1.compose(function2).apply("hello world");
    System.out.printf("str1: %s\n", str1);
    System.out.printf("str2: %s\n", str2);
    // 执行结果
    str1: DLROW OLLEH
    str2: DLROW OLLEH
    
  4. Predicate

    断言型接口,如果满足条件则返回true,否则返回false。

    Predicate predicate1 = s -> String.valueOf(s).length() > 5;
    Predicate predicate2 = s -> String.valueOf(s).startsWith("划");
    boolean flag = predicate1.and(predicate2).test("划过天空的流星");
    System.out.printf("flag: %s", flag);
    
    // 执行结果
    flag: true
    
  5. Supplier

    供给型接口,提供一个返回值。

    Supplier supplier = () -> "爱无悔 情无怨 为何倩影却成烟";
    String str = (String) supplier.get();
    System.out.printf("str: %s", str);
    
    // 执行结果
    str: 爱无悔 情无怨 为何倩影却成烟
    

Stream流

简介

Stream是一个用于处理集合数据的API,它并不是集合,也不是数据结构,其本身并不存储任何元素(或其他地址值)。元素是特定类型的对象,形成一个队列,Stream并不会存储元素,而是按需计算和处理数据。Stream操作有以下特征:

  • 数据源: 流的来源,可以是集合,数组,I/O资源或者其他数据源等。

  • Pipelining:Stream提供了多种方法针对流中的元素进行处理,这些方法支持链式调用形成流水线式的操作流程。每次操作都会返回一个新的流,原数据保持不变。

  • 延迟处理:在调用中间操作方法时,不会立即执行实际计算,只有执行终端操作时才会触发实际的计算。

  • 函数式编程:Stream提供了一组函数式编程的方法,可以以声明的方式操作数据,避免了显式的迭代和条件判断,使代码更简洁、易读。

常用方法

当使用一个流的时候,通常包括三个基本步骤:获取一个数据源(source)->数据转换->执行操作获取想要的结果,每次转换原有Stream对象不变,返回一个新的Stream对象(可以有多次转换),这就允许对其操作可以像链条一样排列,变成一个管道。stream流是一种管道流,只能被消费一次,多次消费同一个流会抛出IllegalStateException异常,链式调用是每次将上一个流传递给下一个流,实际上是生成新的流。

  1. Filter(Predicate<? super T> predicate)

    接收一个Predicate函数作为参数,不满足Predicate条件的会被过滤掉。

    Stream<String> stream = Stream.of("123", "132", "213", "231", "312", "321");
    stream.filter(e -> e.startsWith("3")).forEach(System.out::println);
    stream.filter(Objects::nonNull);
    
    // 执行结果
    312
    321
    // 多次操作一个流抛出异常
    Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
    	at java.base/java.util.stream.AbstractPipeline.<init>(AbstractPipeline.java:203)
    	at java.base/java.util.stream.ReferencePipeline.<init>(ReferencePipeline.java:96)
    	at java.base/java.util.stream.ReferencePipeline$StatelessOp.<init>(ReferencePipeline.java:800)
    	at java.base/java.util.stream.ReferencePipeline$2.<init>(ReferencePipeline.java:167)
    	at java.base/java.util.stream.ReferencePipeline.filter(ReferencePipeline.java:166)
    
  2. map(Function<? super T, ? extends R> mapper)

    接收一个Function函数作为参数,对Stream中的每个元素进行映射转换,返回新的Stream。

    List<String> list0 = Arrays.asList("a", "b", "c", "d", "e", "f");
    list0.stream().map(e -> (int) e.charAt(0)).forEach(System.out::println);
    
    // 执行结果
    97
    98
    99
    100
    101
    102
    
  3. flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)

    扁平映射,接收一个Function函数将流中每个元素映射为一个流并将所有流连接成一个流。

    List<List<Integer>> list1 = Arrays.asList(
            Arrays.asList(1, 2, 3),
            Arrays.asList(4, 5, 6),
            Arrays.asList(7, 8, 9)
    );
    list1.stream().flatMap(List::stream).forEach(System.out::println);
    
    // 执行结果
    123456789
    
  4. distinct()

    根据流中元素的hashCode()和equals()来判断是否重复,并且去重。

    List<Integer> list2 = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1);
    list2.stream().distinct().forEach(System.out::print);
    
    // 执行结果
    1234567
    
  5. sorted() / sorted(Comparator<? super T> comparator)

    用于对流中元素进行排序,默认是按照自然顺序排序,对于无序流不提供稳定性保证。

    也可以接收一个Comparator函数自定义排序规则。

    List<Integer> list3 = Arrays.asList(5, 3, 1, 4, 2, 6, 7);
    list3.stream().sorted().forEach(System.out::print);
    System.out.println();
    list3.stream().sorted(((o1, o2) -> {
        return o2.compareTo(o1);
    })).forEach(System.out::print);
    
    // 执行结果
    1234567
    7654321
    
  6. peek(Consumer<? super T> action)

    类似forEach方法,区别在于这不是终端操作,接收一个Consum函数,对流中每一个元素执行一个操作,返回新的流与原始流中的数据相同

    List<Integer> list4 = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
    list4.stream().peek(e -> {
        e = (e + 3) * 7;
        System.out.printf("value: %d", e);
    }).peek(el -> {
        System.out.printf(" || %d \n", el);
    }).collect(Collectors.toList());
    
    // 执行结果
    value: 28 || 1 
    value: 35 || 2 
    value: 42 || 3 
    value: 49 || 4 
    value: 56 || 5 
    value: 63 || 6 
    value: 70 || 7 
    
  7. limit(long maxSize)

    截断流中的元素个数。

    List<Integer> list5 = Arrays.asList(5, 3, 1, 4, 2, 6, 7);
    list5.stream().limit(3).sorted().forEach(System.out::print);
    
    // 执行结果
    531
    
  8. skip(long n)

    忽略流中前n个元素。

    List<Integer> list6 = Arrays.asList(5, 3, 1, 4, 2, 6, 7);
    list6.stream().skip(3).forEach(System.out::print);
    
    // 执行结果
    4267
    
  9. forEach(Consumer<? super T> action)

    接收一个Consumer函数,对流中每个元素执行一个操作,这是一个终端操作

    List<Integer> list7 = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
    list7.stream().forEach(e -> {
        e *= 2;
        System.out.printf("%d ", e);
    });
    
    // 执行结果
    2 4 6 8 10 12 14 
    
  10. collect(Collector<? super T, A, R> collector)

    接收一个Collector函数,允许重用收集策略和收集操作的组合,这是一个终端操作

    List<Person> list8 = Arrays.asList(
            Person.builder().name("Tom").sex("男").build(),
            Person.builder().name("Jack").sex("男").build(),
            Person.builder().name("Susan").sex("女").build(),
            Person.builder().name("Lilith").sex("女").build()
    );
    Map<String, Map<String, List<Person>>> map = list8.stream().collect(Collectors.groupingBy(Person::getSex, Collectors.groupingBy(Person::getName)));
    map.entrySet().forEach(entry -> {
        System.out.printf("%s:%s \n", entry.getKey(), entry.getValue());
    });
    
    // 执行结果:{Lilith=[Person(name=Lilith, sex=)], Susan=[Person(name=Susan, sex=)]}:{Tom=[Person(name=Tom, sex=)], Jack=[Person(name=Jack, sex=)]} 
    
  11. count()

    返回流中元素个数,这是一个终端操作

    List<Integer> list9 = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
    long count = list9.stream().count();
    System.out.printf("count:%d", count);
    
    // 执行结果
    count:7
    
  12. anyMatch(Predicate<? super T> predicate) / allMatch(Predicate<? super T> predicate) / noneMatch(Predicate<? super T> predicate)

    anyMatch方法用于判断流中元素是否存在至少一个满足给定的条件,返回boolean值,这是一个短路操作[^1]

    List<String> list10 = Arrays.asList("12", "23", "34", "45", "56", "67");
    boolean result = list10.stream().anyMatch(s -> s.length() > 3);
    System.out.printf("result: %s", result);
    
    // 执行结果
    result: false
    

    allMatch方法用于判断流中元素是否全部满足给定的条件,返回boolean值,这是一个短路操作

    List<String> list10 = Arrays.asList("12", "23", "34", "45", "56", "67");
    result = list10.stream().allMatch(s -> s.length() >= 2);
    System.out.printf("result: %s", result);
    
    // 执行结果
    result: true
    

    noneMatch方法用于判断流中元素是否没有给定的条件,返回boolean值,这是一个短路操作

    List<String> list10 = Arrays.asList("12", "23", "34", "45", "56", "67");
    result = list10.stream().noneMatch(s -> s.length() > 2);
    System.out.printf("result: %s", result);
    
    // 执行结果
    result: true
    

    短路操作:对于流中的元素,只要满足条件之后即可返回,无需操作所有元素

Optional

Optional是一个容器类工具类,目的是解决NPE问题。
Optional是一个包装器类,其中包含对其他对象的引用,这样就不用显式的进行空值检测。

创建Optional对象

  1. empty()

    Optional<String> empty = Optional.empty();
    System.out.println(empty);
    // 执行结果
    Optional.empty
    
  2. of(T value)

    此处参数必须不为null,否则会报错NPE;

    String str = null;
    Optional<String> stringOptional = Optional.of(str);
    
    // 执行结果
    Exception in thread "main" java.lang.NullPointerException
    	at java.base/java.util.Objects.requireNonNull(Objects.java:208)
    	at java.base/java.util.Optional.of(Optional.java:113)
    
  3. ofNullable(T value)

    如果参数为null,则会返回空的Optional对象。

    String str = null;
    Optional<String> stringOptional = Optional.ofNullable(str);
    System.out.println(stringOptional);
    
    // 执行结果
    Optional.empty
    

常用方法

  1. isPresent()

    判断一个Optional对象是否存在,如果存在则返回true,否则返回false。

    String str = null;
    Optional<String> stringOptional = Optional.ofNullable(str);
    System.out.println(stringOptional.isPresent());
    str = new String("Hello World");
    System.out.println(Optional.ofNullable(str).isPresent());
    
    // 执行结果
    false
    true
    
  2. isEmpty()

    类似isPresen(),如果对象存在就返回false,否则返回true。

    String str = null;
    Optional<String> stringOptional = Optional.ofNullable(str);
    System.out.println(stringOptional.isEmpty());
    str = new String("Hello World");
    System.out.println(Optional.ofNullable(str).isEmpty());
    
    // 执行结果
    true
    false
    
  3. ifPresent(Consumer<? super T> action)

    接收一个Consumer函数,如果存在值则执行传入的操作。

    Optional.ofNullable("Hello World").ifPresent(s -> System.out.printf("%s!", s));
    Optional.ofNullable(null).ifPresent(s -> System.out.printf("%s!", s)); 
    
    // 执行结果
    Hello World!
    
  4. ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction)

    接收Consumer和Rnnable两个函数,如果存在值就执行第一个,否则就执行第二个操作。

    Optional.ofNullable("Hello World").ifPresentOrElse(s -> System.out.printf("%s!\n", s), () -> System.out.println("value不存在"));
    Optional.ofNullable(null).ifPresentOrElse(s -> System.out.printf("%s!", s), () -> System.out.println("value不存在"));
    
    // 执行结果
    Hello World!
    value不存在
    
  5. filter(Predicate<? super T> predicate)

    接收一个Predicate函数,如果满足条件则返回包含该对象的Optional对象,否则返回一个空的Optional对象。

    Optional<String> optional0 = Optional.ofNullable("Hello World").filter(s -> s.length() > 5);
    Optional<String> optional1 = Optional.ofNullable("Hello World").filter(s -> s.length() > 11);
    System.out.printf("optional0: %s\n", optional0);
    System.out.printf("optional1: %s\n", optional1);
    
    // 执行结果
    optional0: Optional[Hello World]
    optional1: Optional.empty
    
  6. map(Function<? super T, ? extends U> mapper)

    接收一个Function函数,如果值存在则返回一个包含映射结果值的Optional,否则返回一个空的Optional。

    Optional<String> nameOptional = Optional.ofNullable(new Person("Jerry", "男")).map(Person::getName);
    System.out.printf("nameOptional: %s", nameOptional);
    
    // 执行结果
    nameOptional: Optional[Jerry]
    
  7. flatMap(Function<? super T, ? extends Optional<? extends U>> mapper)

    接收一个Function的函数,将 Optional 对象中的值映射为另一个 Optional 对象,然后将两个 Optional 对象合并为一个返回。

    Optional<String> nameUpper = Optional.ofNullable(new Person("Jerry", "男")).flatMap(e -> Optional.ofNullable(e.getName().toUpperCase()));
    System.out.printf("nameUpper: %s", nameUpper);
    
    // 执行结果
    nameUpper: Optional[JERRY]
    
  8. orElse(T other)

    如果Optional的对象是null则返回该值。

    String string = (String) Optional.ofNullable(null).orElse("Hello World");
    System.out.printf("string: %s", string);
    
    // 执行结果
    string: Hello World
    
  9. orElseGet(Supplier<? extends T> supplier)

    接收一个Supplier函数,如果Optional的对象是null则执行该操作。

    String name = (String) Optional.ofNullable(null).orElseGet(new Person("Jerry", "男")::getName);
    System.out.printf("name: %s", name);
    
    // 执行结果
    name: Jerry
    
  10. orElseThrow(Supplier<? extends X> exceptionSupplier)

    接收一个Supplier函数,如果Optional的对象是null则抛出自定义异常。

    try {
        Optional.ofNullable(null).orElseThrow(() -> new Exception("npe"));
    }catch (Exception e) {
        e.printStackTrace();
    }
    
    // 执行结果
    java.lang.Exception: npe
    
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java 8引入了Function接口和Lambda表达式,这些特性使得函数式编程变得更加容易和灵活。 Function接口是一个函数式接口,它定义了一个接受一个参数并返回一个结果的函数。它有一个抽象方法apply(),这个方法接受一个参数并返回一个结果。Function接口有多个默认方法可以组合成复杂的函数链,比如andThen()和compose()方法。 Lambda表达式是一种可以代替匿名内部类的语法,它可以用来创建函数式接口的实例。Lambda表达式由三个部分组成:参数列表、箭头符号和方法体。Lambda表达式的参数列表和方法体可以根据上下文自动推断,这使得代码更加简洁易读。 下面是一个使用Function和Lambda表达式的示例代码: ``` Function<Integer, Integer> square = x -> x * x; int result = square.apply(5); // result = 25 ``` 这个代码定义了一个Function接口的实例square,它接受一个整数参数并返回它的平方。然后我们可以调用这个实例的apply()方法来计算5的平方,结果为25。 Lambda表达式可以用来代替匿名内部类,使得代码更加简洁易读。比如下面这个代码: ``` Runnable runnable = new Runnable() { @Override public void run() { System.out.println("Hello, world!"); } }; ``` 可以使用Lambda表达式来简化为: ``` Runnable runnable = () -> System.out.println("Hello, world!"); ``` 这个代码定义了一个Runnable接口的实例runnable,它的run()方法会输出一条消息。Lambda表达式的箭头符号左侧没有参数,因为run()方法不接受任何参数。箭头符号右侧的方法体只有一条语句,因此可以省略大括号和分号。 总之,Java 8的Function和Lambda表达式为函数式编程带来了更加灵活和易用的工具,使得Java程序员可以更加方便地编写函数式风格的代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值