响应式编程一之基础夯实(初学必看!)

2023-12-13 04:11:02

函数式编程

1、可以不用关注其细节【怎么做】,只需要关注命令即可【做什么】
2、可以使代码更加简洁

常见lambda表达式

求一个数组里面的最小值

public class MinDemo {
    public static void main(String[] args) {
        int[] nums = {12,23,34,78,67,24};
        int min = Integer.MAX_VALUE;
        for(int i : nums){
            if(i < min){
                min = i;
            }
        }
        System.out.println(min);

        // jdk8
        //int的stream流然后在min方法
        int min2 = IntStream.of(nums).min().getAsInt();
        System.out.println(min2);
    }
}

假设数组数据有几亿条数据,那么我们不用函数式编程的思想可能考虑的优化做法为使用多线程,创建一个线程池,然后将数组里面的数据进行拆分,然后采用快排的分治的思想进行解决。【比较麻烦】
而函数式编程则可以使用

   int min2 = IntStream.of(nums).parallel().min().getAsInt();

代码简洁的函数式编程

   new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("命令式编程");
            }
        }).start();
        //函数式编程方式
        new Thread(()-> System.out.println("函数式编程"));

返回指定对象的接口实例

  public static void main(String[] args) {
        Object target1 = new Runnable() {
            @Override
            public void run() {
                System.out.println("命令式编程");
            }
        };
        new Thread((Runnable) target1).start();

        //jdk8
        Object target2 = (Runnable)()-> System.out.println("函数式编程");
        Runnable target3 = ()-> System.out.println("函数式编程");
        System.out.println(target2 == target3);
        new Thread((Runnable) target2).start();
    }
interface Interface1{
    int doubleNum(int i);
    default int add(int x,int y){
        return x + y;
    }
}
public class LambdaDemo1 {
    public static void main(String[] args) {

        Interface1 i1 = (i) -> i*2;
        i1.add(3,7);
        i1.doubleNum(12);

        //这是最常用的一种方式
        Interface1 i2 = i -> i *2;

        Interface1 i3 =(int i) ->  i *2;

        Interface1 i4 = (int i) ->{
            System.out.println("lambda 表达式使用");
            return i *2;
        };

    }
}

JDK8 新特性

1)函数式接口:接口只有一个需要实现的方法(@FunctionalInterface)
可以通过函数式接口将接口尽可能的封装的更细致一些,符合设计模式中的但一职责规范,然后接口与接口之间进行多重继承,抽取一个类把接口都给继承一遍
2)默认方法 通过default修饰

@FunctionalInterface
interface Interface1 {
    int doubleNum(int i);

    default int add(int x, int y) {
        return x + y;
    }
}


public class LambdaDemo1 {
    public static void main(String[] args) {

        Interface1 i1 = (i) -> i * 2;
        i1.add(3, 7);
        i1.doubleNum(12);

        //这是最常用的一种方式
        Interface1 i2 = i -> i * 2;

        Interface1 i3 = (int i) -> i * 2;

        Interface1 i4 = (int i) -> {
            System.out.println("lambda 表达式使用");
            return i * 2;
        };

    }
}

jdk8函数式接口

通过如下代码将money格式化

interface IMoneyFormat{
    String format(int i );
}
class MyMoney{
    private final int money;
    public MyMoney(int money){
        this.money =  money;
    }
    public void printMoney(IMoneyFormat moneyFormat){
        System.out.println("我的存款:" + moneyFormat.format(this.money));
    }

}
public class MoneyDemo {
    public static void main(String[] args) {
        MyMoney me = new MyMoney(999999999);
        me.printMoney(i ->new DecimalFormat(",###").format(i));
 
  }
}

第二种写法用Function

interface IMoneyFormat{
    String format(int i );
}
class MyMoney{
    private final int money;
    public MyMoney(int money){
        this.money =  money;
    }
     public void printMoney(Function<Integer,String> moneyFormat){
        System.out.println("我的存款:" + moneyFormat.apply(this.money));
    }

}
public class MoneyDemo {
    public static void main(String[] args) {
        MyMoney me = new MyMoney(9999999);
          me.printMoney(i -> new DecimalFormat(",###").format(i));
        // 我们学习lambda表达式就可以这样写
     //   Function<Integer,String > moneyFormate=i -> new DecimalFormat("#.###").format(i);
         // 函数接口链式操作
//        me.printMoney(moneyFormate.andThen(s->"人民币" +s));
    }
}

predicate 判断hashmap是否为空

 public static boolean isNullOrEmpty(Map map) {

        //这里需要指定范型,这里是Map,指定好了范型后,方法里面才能用Map对象的方法
        Predicate<Map> predicate = (map1)->{
            if (null == map1 || 0 == map1.size()) {
                System.out.println("map 的 size is 0");
                return true;
            } else {
                return false;
            }
        };
        return predicate.test(map);
    }

    public static void main(String[] args) {
        Map map = new HashMap();
        //使用predicate方法
        System.out.println( isNullOrEmpty(map));
    }

consumer

   //客户需要购物
    public static void shopping(double x, Consumer consumer) {
        consumer.accept(x);
    }

    public static void main(String[] args) {
        Consumer<Double> c1 = (x) -> {
            System.out.println("小李花了" + x + "元,买了一辆车");
        };
        shopping(20000, c1);

        Consumer<Double> c2 = (x) -> {
            System.out.println("小明花了" + x + "元,买了一堆化妆品");
        };
        shopping(50000, c2);
    }

总结

在这里插入图片描述
前五个对应一个输入一个输出,没有输出是消费者,没有输入是生产者。

方法引用示例

import java.util.function.*;

class Dog{
    private String name= "哮天犬";
    private int food =10;

    public static void bark(Dog dog){
        System.out.println(dog +"叫了");
    }

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

    }
    @Override
    public String toString() {
        return this.name;
    }

    /**
     * 吃狗粮,静态方法
     * 接下来我们对这成员方法进行成员引用,成员引用套路都是一样的,就是分析他的输入和输出,输入一个int输出一个int
     * @param this  JDK默认会把当前实例传入到非静态方法,参数名为this,位置是第一个
     * @param num
     * @return
     */

    public int eat(Dog this,int num){
        System.out.println("吃了" +num+ "斤狗粮");
        this.food = this.food - num;
        return this.food;
    }
}
public class MethodRefrenceDemo {
    public static void main(String[] args) {
        // 这就是一个lambda表达式,箭头左边是输入参数,箭头右边是函数的执行体
        // 当你的函数执行体里面只有一个函数进行调用,而且函数的参数和传入参数是一样的话,我们就缩写成
       Consumer<String> consumer = s-> System.out.println(s);
       consumer.accept("接收数据1");
        // 方法引用的方式
        Consumer<String> consumer2 = System.out::println;
        consumer2.accept("接受数据");
       //静态方法的方法引用
        Dog dog = new Dog();
        Consumer<Dog> consumer3 = Dog::bark;
        consumer3.accept(dog);

        // 非静态方法,使用对象实例来引用
        Function<Integer,Integer> funtion = dog::eat;
        System.out.println("还剩下" +funtion.apply(2)+ "斤");
        // 演进 1 当输入和输入都是相同类型则可以用一元函数
        UnaryOperator<Integer> unaryOperator = dog::eat;
        System.out.println("还剩下" +unaryOperator.apply(2)+ "斤");
        // 演进2 :因为我们这都是Integer,所以JDK8针对类型也有对应的函数接口
        IntUnaryOperator intUnaryOperator = dog::eat;
        System.out.println("还剩下" +intUnaryOperator.applyAsInt(2)+ "斤");

        // 静态方法和成员方法有一个区别是什么呢?
        // 成员方法可以调用this, 静态方法不能调用this ,那jdk是怎样实现的的,他是在方法里面传递了this
        Dog dog1 = new  Dog();
        dog1.eat(23);

        // 使用类名来方法引用
        BiFunction<Dog,Integer,Integer> eatFunction = Dog::eat;
        System.out.println(eatFunction.apply(dog1,2));

        // 构造函数的方法引用
        // 无构造参数
        // 我们dog里面没有对应的构造方法,我们默认是空的构造方法
        // 这也就是传入是空的,返回一个结果也就生产者
        Supplier<Dog> supplier  = Dog::new;
        System.out.println("创建了新对象:" +supplier.get());
        // 有参数的构造方法
        // 我们这里是和上面一样的,我们的JDK会自动找到有对应参数的
        Function<String,Dog> fun = Dog::new;
        System.out.println(fun.apply("警犬"));


    }
}

lambda表达式的类型推断

之前说过lambda表达式是一个匿名函数,它最后返回来的是实现某个接口的对象。

@FunctionalInterface
interface IMath{
    int add(int x,int y);
}

@FunctionalInterface
interface IMath2{
    int add(int x,int y);
}

public class TypeDemo {
    public static void main(String[] args) {
       // 变量类型定义
       IMath lambda = (x,y) -> x +y;
       // 强转
        Object lambda2 = (IMath) (x,y) -> x + y;
        //通过返回类型
        IMath createLambda = createLambda();
        // 实际我们编写的是后都是在方法里面调用的
        TypeDemo demo = new TypeDemo();
       // demo.test((x,y) ->x +y);
        // 注意:我们这里如果重载的话就会有问题,此时我们可以
        //通过强制类型转化
        demo.test((IMath) (x,y) ->x +y);
    }

    public void test(IMath iMath){

    }
    public void test(IMath2 iMath){

    }
    private static IMath createLambda() {
        return  (x,y) -> x +y;
    }
}

变量引用

consumer是作为值传递的外面定义的变量

/**
 * Lambda表达式是实现某个接口的匿名类,那他引用变量和我们匿名类是相同的
 */
public class VarDemo {
    public static void main(String[] args) {
        //此处默认加了一个final
        String str = "欢迎您";
        //str = "SSS";
        Consumer<String> consumer = s -> System.out.println(s + str);
        consumer.accept("北京");
        // jdk8之前内部类引用外面的变量,这变量必须声明为final类型,
        // 那jdk8里面默认可以不写,他默认增加了final
        // 匿名类引用外面的变量必须是final
        // 因为我们java方法传参的形式,是传的值而不是引用
        List<String> list =new ArrayList<>();
        Consumer<String> consumer2 = s -> System.out.println(s + list);
        consumer.accept("北京");
    }
}

Stream流

内部迭代和外部迭代

     int[] nums = {1,2,3};
        // 外部迭代
        int sum1 = 0;
        for(int i : nums){
            sum1 += i;
        }
        System.out.println("结果为:" + sum1);
        // 使用Stream的内部迭代
        //比较简短
        int sum2 = IntStream.of(nums).sum();
        System.out.println("结果为:" + sum2);

中间操作和终止操作

    public static void main(String[] args) {
        int[] nums = {1,2,3};
        // map 是中间操作 (返回Stream的操作)
        // sum 是终止操作
        int sum3 = IntStream.of(nums).map(StreamDemo1::doubleNum).sum();
        System.out.println("结果为:" + sum3);
        System.out.println("惰性求值就是最终没有调用的情况下,中间操作不会执行");
        //此处不会输出执行了乘以2
        IntStream.of(nums).map(StreamDemo1::doubleNum);
    }
    public static int doubleNum(int i){
        System.out.println("执行了乘以2");
        return i * 2;
    }

Stream流编程-创建流的方式

在这里插入图片描述

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        // 从集合创建流
        list.stream();
        list.parallelStream();

        // 从数组创建流
        Arrays.stream(new int[]{1,2,3,4,5});

        // 创建数字流
        IntStream.of(1,2,3,4);
        IntStream.rangeClosed(1,10);

        //使用random创建一个无限流
        new Random().ints().limit(10);
        Random random = new Random();
        // 自己产生流
        Stream.generate(() -> random.nextInt()).limit(20);

    }

Stream流编程-中间操作

在这里插入图片描述
无状态操作:当前的操作和其他前后的操作无关
有状态操作:当前结果依赖于其他的结果

    String str = "my name is Jack";
        // 把每个单词的长度调用出来
       Stream.of(str.split(" ")).map(s ->s.length()).forEach(System.out::println);
       //筛选出长度大于2的单词
      Stream.of(str.split(" ")).filter(s -> s.length() > 2)
                .map(s -> s.length()).forEach(System.out::println);
        // flatMap A -> B属性(是个集合),最终得到所有的A元素里面的所有B属性集合
        // intStream/longStream 并不是Stream的子类,所以要进行装箱
        Stream.of(str.split(" ")).flatMap(s -> s.chars().boxed() ).forEach(System.out::println);
 Stream.of(str.split(" ")).flatMap(s -> s.chars().boxed()).forEach(
                i -> System.out.println((char)i.intValue()));
//        // peek 用于debug,是中间操作和forEach 是终止操作
        System.out.println("=============peek===============");
        Stream.of(str.split(" ")).peek(System.out::println).forEach(System.out::println);
        // limit 使用,主要用于无限流
        // 1、我们不做限制的看看他是否出现异常,这里没有异常,好像一直不会结束
        new Random().ints().forEach(System.out::println);
        new Random().ints().filter(i -> i > 100 && i < 1000).limit(3)
                .forEach(System.out::println);

Stream流编程-终止操作

在这里插入图片描述

       // 使用并行流,我们打印一下发现是乱序的
        str.chars().parallel().forEach(i -> System.out.print((char)i));
        System.out.println();
//        // 使用 forEachOrdered保证顺序
        str.chars().parallel().forEachOrdered(i -> System.out.print((char)i));
 //第二行:collect/toArray我们可以看到他是一个收集的作用,他可以转成数组或者集合
        // 收集到list
        List<String> list = Stream.of(str.split(" ")).collect(Collectors.toList());
        // 第三个是reduce操作,reduce就是减少的意思,他就是将流合成一个数据,
       //使用reduce拼接字符串
          // 这里返回一个optional 这也是新jdk8添加的, optional是选项的意思,避免自己调用一些空判断,所以我们一般是这样使用
        Optional<String> letters = Stream.of(str.split(" "))
                .reduce((s1, s2) -> s1 + "|" + s2);
        System.out.println(letters.orElse(""));

   // 带初始化值的reduce
        String reduce = Stream.of(str.split(" "))
                .reduce("", (s1, s2) -> s1 + "|" + s2);
        System.out.println(reduce);

  //这就是字符串总长度
       Integer length = Stream.of(str.split(" ")).map(s ->s.length())
                .reduce(0, (s1, s2) -> s1 +  s2);
        System.out.println(length);
    // 使用max
        Optional<String> max = Stream.of(str.split(" ")).max((s1, s2) -> s1.length() - s2.length());
        System.out.println(max.get());
     // 使用findFirst短路操作
        OptionalInt any = new Random().ints().findAny();
        System.out.println(any.getAsInt());

        OptionalInt first = new Random().ints().findFirst();
        System.out.println(first.getAsInt());
    // allMatch
        Student stu1 = new Student( 19, "张三");
        Student stu2 = new Student( 23, "李四");
        Student stu3 = new Student( 28, "王五");
        List<Student> students = new ArrayList<>();
        students.add(stu1);
        students.add(stu2);
        students.add(stu3);
        //判断学生年龄是否都大于18
        boolean flag = students.stream().allMatch(stu -> stu.getAge() > 18);
        System.out.println(flag);

并行流


  public static void debug(int i){
        System.out.println(Thread.currentThread().getName() + "  debug " + i);
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void debug2(int i){
        System.out.println("  debug2 " + i);
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
     //1、调用 顺序进行调用(串行)
         IntStream.range(1,100).peek(StreamDemo5::debug).count();
        //2、调用 paralled产生一个并行流(并行)
          IntStream.range(1,100).parallel().peek(StreamDemo5::debug).count();
        //3、现在要实现一个这样的效果: 先并行,再串行
        // 多次调用 parallel / sequential 以最后一次为准,下例子是串行的
        IntStream.range(1,100)
                // 调用paralled产生并行流
                .parallel().peek(StreamDemo5::debug)
//                // 调用sequential 产生串行流
                .sequential().peek(StreamDemo5::debug2)
                .count();
        // 4.并行流使用的线程池:ForkJoinPool.commonPool
            IntStream.range(1,100).parallel().peek(StreamDemo5::debug).count();

    public static void debug(int i){
        System.out.println(Thread.currentThread().getName() + "  debug " + i);
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
  // 5、我们现在并行流都是用的同一个线程池,那就会有一下问题:
        //  多个并行任务用同一个线程池,就会有排队等待的情况,这时候我们就需要自己创建线程池
        // 防止任务被阻塞
         ForkJoinPool pool = new ForkJoinPool(20);
        pool.submit(() -> IntStream.range(1,100).parallel().peek(StreamDemo5::debug).count());
        // 处理完毕线程池会自动关闭
        pool.shutdown();
        // 我们实现将自己的任务放到自己并行线程池里面,我们测试一下
        // 发现控制台并没有任何日志打印出来
        // 这是因为主线程已经停了,我们线程池是守护线程的关系,所以他也自动退出了,那我们需要让我们的主线程不要退出

        synchronized (pool){
            try {
                pool.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 运行之后发现用的是我们自己的线程池 ForkJoinPool-1

收集器

public class Person {
    private String name;
    private int age;
    private Gender gender;
    private Grade grade;

    public Person(String name, int age, Gender gender, Grade grade) {
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.grade = grade;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", gender=" + gender +
                ", grade=" + grade +
                '}';
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Gender getGender() {
        return gender;
    }

    public void setGender(Gender gender) {
        this.gender = gender;
    }

    public Grade getGrade() {
        return grade;
    }

    public void setGrade(Grade grade) {
        this.grade = grade;
    }

    enum Gender{
        MALE,FEMALE
    }
    enum Grade{
        ONE,TWO,THREE,FOUR;
    }

    public static void main(String[] args) {
        List<Person> peoples = Arrays.asList(
                new Person("小明",10,Gender.MALE,Grade.ONE),
                new Person("小林",12,Gender.FEMALE,Grade.ONE),
                new Person("李刚",13,Gender.MALE,Grade.TWO),
                new Person("小花",14,Gender.MALE,Grade.TWO),
                new Person("小兰",5,Gender.FEMALE,Grade.THREE),
                new Person("天涯",16,Gender.MALE,Grade.ONE),
                new Person("善缘",16,Gender.MALE,Grade.TWO),
                new Person("谷歌",43,Gender.FEMALE,Grade.FOUR),
                new Person("黎明",33,Gender.MALE,Grade.ONE)
        );

        // 1、得到所有学生的年龄列表
        // 或者我们可以转化为:Collectors.toSet() ,或者指定的集合类型:他默认是hashSet我们如果用TreeSet
        // 可以如下:Collectors.toCollection(TreeSet::new )
        List<Integer> ages = peoples.stream().map(s -> s.getAge()).collect(Collectors.toList());
        System.out.println("所有学生的年龄 :" + ages);
       // 1、统计汇总信息
        IntSummaryStatistics ageSummaryStatistics = peoples.stream().collect(Collectors.summarizingInt(Person::getAge));
        System.out.println("年龄汇总:" + ageSummaryStatistics);
        //2、分块
        Map<Boolean, List<Person>> genders = peoples.stream().
                collect(Collectors.partitioningBy(s -> s.getGender() == Gender.MALE));
        //
        //System.out.println( "男女学生列表:" + genders);
        // 上述打印不直观
        MapUtils.verbosePrint(System.out,"男女学生列表",genders);
        // 3、分组
        Map<Grade, List<Person>> grades = peoples.stream()
                .collect(Collectors.groupingBy(Person::getGrade));
        MapUtils.verbosePrint(System.out,"学习班级列表",grades);
        // 4、得到所有班级的个数
        Map<Grade, Long> gradesCount = peoples.stream()
                .collect(Collectors.groupingBy(Person::getGrade,Collectors.counting()));
        MapUtils.verbosePrint(System.out,"班级学生个数列表:",gradesCount);

    }
}

引入pom

    <dependencies>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-collections4</artifactId>
            <version>4.4</version>
        </dependency>
    </dependencies>

文章来源:https://blog.csdn.net/lsdstone/article/details/134901468
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。