常用函数式接口

10/16/2022 Java

# 常用函数式接口

# 概述

只有一个抽象方法的接口我们称之为函数接口,不包含default默认的方法。

其中JDK自带的函数式接口还加上了@FunctionalInterface注解进行标识。

# 常见的函数式接口

image-20231218142627583

# Consumer 消费接口

根据抽象方法的参数列表和返回类型知道,我们可以在方法中对传入的参数进行消费(操作)

image-20221011210531793

# Function 计算转换接口

我们可以在方法中对传入的参数计算或转化,把结果返回

image-20221011211633418

# Predicate 判断接口

我们可以在方法中对传入的参数条件判断,返回判断结果。

image-20221011211824243

# supplier 生产型接口

我们可以在方法中创建对象,把创建好的对象返回。

image-20221011212019931

# 常用的默认方法

# and

我们在使用Predicate接口时候可能需要进行判断条件的拼接。而and方法相当于是使用**&&**来拼接两个条件。

//打印输出所有成年且作品在一部以上的作家名字
authors.stream()
        .filter(author -> author.getAge()>18)
        .filter(author -> author.getBooks().size()>1)
        .map(author -> author.getName())
        .forEach(name-> System.out.println(name));

authors.stream()
        .filter(author -> author.getAge()>18&&author.getBooks().size()>1)
        .map(author -> author.getName())
        .forEach(name-> System.out.println(name));

//使用接口
authors.stream()
        .filter(new Predicate<Author>() {
            @Override
            public boolean test(Author author) {
                return author.getAge()>18;
            }
        }.and(new Predicate<Author>() {
                    @Override
                    public boolean test(Author author) {
                        return author.getBooks().size()>1;
                    }
                })
        ).map(author -> author.getName())
        .forEach(name-> System.out.println(name));

//使用Lambda表达式
authors.stream()
        .filter(((Predicate<Author>) author -> author.getAge() > 18).and(author -> author.getBooks().size()>1)
        ).map(author -> author.getName())
        .forEach(name-> System.out.println(name));

# or

我们在使用Predicate接口时候可能需要进行判断条件的拼接。而and方法相当于是使用||来拼接两个判断条件。

//打印输出未成年或作品大于1的作家信息 
authors.stream().filter(new Predicate<Author>() {
            @Override
            public boolean test(Author author) {
                return author.getAge()<18;
            }
        }.or(new Predicate<Author>() {
            @Override
            public boolean test(Author author) {
                return author.getBooks().size()>1;
            }
        })).forEach(author -> System.out.println(author));

        authors.stream().filter(((Predicate<Author>) author -> author.getAge() < 18)
                .or(author -> author.getBooks().size()>1))
                .forEach(author -> System.out.println(author));

# negate

//打印出不小于18岁的作家姓名
authors.stream()
                .filter(new Predicate<Author>() {
                    @Override
                    public boolean test(Author author) {
                        return author.getAge()<18;
                    }
                }.negate()).map(author -> author.getName())
                .forEach(name-> System.out.println(name));



        authors.stream()
                .filter(((Predicate<Author>) author -> author.getAge() > 18).negate()
                ).map(author -> author.getName())
                .forEach(name-> System.out.println(name));

# 方法引用

我们使用Lambda表达式时,如果方法体中只有一个方法调用的话,我们可以进一步简化代码。

# 基本格式

类名或者对象名::方法名

        authors.stream()
                .filter(new Predicate<Author>() {
                    @Override
                    public boolean test(Author author) {
                        return author.getAge()<18;
                    }
                }.negate()).map(Author::getName)
                .forEach(name-> System.out.println(name));

# 基本类型优化

使用Stream的方法由于都是用到泛型,所以涉及到的参数和返回值都是引用类型。

我们操作整数小数,实际上使用的是他们的包装类。当数据很多的时候自动拆装箱非常浪费资源。

所以为了避免这些造成的时间消耗,Stream提供了很多专门针对于基本数据类型的方法。

例如:mapToIntmapToLongmapToDoubleflapMapToIntflapMapToDouble

        authors.stream()
                .map(author -> author.getAge())
                .map(age -> age+10)
                .forEach(System.out::println);

		//避免后续操作的拆装箱
        authors.stream()
                .mapToInt(author -> author.getAge())
                .map(age -> age+10)
                .forEach(System.out::println);

# 并行流

当流中有大量元素时,我们可以使用并行流去提高操作效率。并行流分配给多个线程,而且不用考虑线程安全问题,很方便。

 //计算总年龄
        int asInt = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .peek(book -> System.out.println(book.getId()+ Thread.currentThread().getName()))
                .mapToInt(book -> book.getScore())
                .reduce((result, ele) -> result + ele)
                .getAsInt();
        System.out.println(asInt);

1main
2main
3main
4main
5main
471
//计算总年龄
        int asInt1 = authors.parallelStream()
                .flatMap(author -> author.getBooks().stream())
                .peek(book -> System.out.println(book.getId()+ Thread.currentThread().getName()))
                .mapToInt(book -> book.getScore())
                .reduce((result, ele) -> result + ele)
                .getAsInt();
        System.out.println(asInt1);

4main
5main
1ForkJoinPool.commonPool-worker-25
2ForkJoinPool.commonPool-worker-25
3ForkJoinPool.commonPool-worker-25
471

# Lambda表达式懒加载

lambda 表达式的重点是延迟执行 (deferred execution)

好处:

  • 在一个单独的线程中运行代码;

  • 多次运行代码;

  • 在算法的适当位置运行代码(例如,排序中的比较操作);

  • 发生某种情况时运行代码(如,点击了一个按钮,数据已经到达,等等);

  • 只在必要时才运行代码。

package com.yuwei.reflect;


import java.time.LocalDateTime;
import java.util.function.Supplier;

public class AATest {
    public static void main(String[] args) {
        System.out.println(LocalDateTime.now());
        lambda1(true,testString());
        System.out.println(LocalDateTime.now());
        lambda1(false,testString());
        System.out.println(LocalDateTime.now());
        lambda2(true,()->testString());
        System.out.println(LocalDateTime.now());
        lambda2(false,()->testString());
        System.out.println(LocalDateTime.now());
    }

    public static String testString(){
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("调用testString");
        return "testString";
    }

    public static void lambda1(boolean flag,String msg){
        if(flag){
            System.out.println(msg);
        }
    }

    public static void lambda2(boolean flag, Supplier<String> supplier){
        if(flag){
            supplier.get();
        }
    }
}

如上面代码所示,

调用lambda1(false,testString());不管第一个参数是不是true,都会调用testString()。当testString()用的时间过长的话,非常影响性能。用lambda表达式即可解决该问题。

2023-12-18T13:42:55.350
调用testString
testString
2023-12-18T13:42:57.353
调用testString
2023-12-18T13:42:59.362
调用testString
2023-12-18T13:43:01.370
2023-12-18T13:43:01.370
更新时间: 12/18/2023, 2:37:15 PM
А зори здесь тихие-тихие
Lube