• Java 复习笔记 - Lambda 表达式 he 经典算法题



    Lambda表达式 概述

    Lambda 表达式(Lambda Expressions)是一种简洁的表示匿名函数(anonymous function)的方法,它允许将函数作为方法的参数(MOP,Method Ordered Parameters)或者代码中的一部分进行传递。这种表达式在 Python、JavaScript、Java 等多种编程语言中都有应用。

    (一)基本作用

    Lambda表达式在Java和其他函数式编程语言中有着广泛的应用。其主要作用包括:

    1. 简化代码:Lambda表达式可以作为参数传递给其他函数,或者在函数中创建简短、可读性更高的代码,这样可以帮助减少代码量,并提升代码的可读性和可维护性。
    2. 函数式编程:Lambda表达式使得函数式编程在Java中变得可能。函数式编程强调将计算作为数学上的函数求值,避免改变状态和使用可变数据。通过Lambda表达式,可以更方便地实现函数式接口。
    3. 匿名函数:Lambda表达式是一种匿名函数,可以作为参数传递给其他函数,或者在函数中作为代码片段直接使用,而不需要定义一个显式的函数名称。
    4. 提升代码可维护性:由于Lambda表达式可以使代码更加简洁和模块化,这使得代码更容易理解和维护。

    总的来说,Lambda表达式是一种强大的工具,可以使代码更简洁,更易读,更模块化,从而提升代码的可维护性和可重用性。

    (二)特点

    Lambda表达式是Java 8中最重要的新功能之一,它有以下几个特点:

    1. 函数式编程:Lambda表达式可以替代只有一个抽象函数的接口实现,它是一种函数式编程的方式,可以帮助我们编写更简洁易懂的代码。
    2. 参数类型自动推断:Lambda表达式中的参数类型可以自动推断,不需要显式地指定类型。
    3. 代码量少、简洁:使用Lambda表达式可以将代码量减少,使代码更加简洁易懂。Lambda表达式的语法也很简洁,易于编写和阅读。

    Lambda表达式的应用场景非常广泛,它可以应用于任何有函数式接口的地方,例如集合、框架的迭代、遍历、过滤数据的操作等。

    一,初识Java中的Lambda 表达式

    下面是一个简单的Java Lambda表达式的示例,它使用Lambda表达式来实现一个简单的接口方法:

    interface MyInterface {
        void doSomething();
    }
    
    public class LambdaExample {
        public static void main(String[] args) {
            // 使用Lambda表达式实现MyInterface接口的doSomething方法
            MyInterface myLambda = () -> System.out.println("Hello Lambda!");
            
            // 调用MyInterface接口的doSomething方法
            myLambda.doSomething();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    输出:

    Hello Lambda!
    
    • 1

    在这个示例中,定义了一个MyInterface接口,它只有一个没有参数的doSomething方法。然后在main方法中使用Lambda表达式来创建一个实现该接口的对象,并将其赋值给myLambda变量。最后,调用myLambda对象的doSomething方法,该方法会输出“Hello Lambda!”。

    二,函数式编程

    函数式编程是一种编程范式,它将计算视为数学上的函数求值,并避免改变状态和使用可变数据。在 Java 中,函数式编程的主要特点包括:

    1. 不可变性(Immutability): 在函数式编程中,一旦一个变量被赋值,你就不能改变它。这意味着你不能更改变量的状态,这也被称为“不可变性”。
    2. 纯函数(Pure Functions): 纯函数是一种既不改变外部状态也不依赖外部状态的函数,它给定相同的输入就会产生相同的输出,而且没有任何副作用。
    3. 高阶函数(Higher-Order Functions): 高阶函数是接受一个或多个函数作为输入(参数)或返回一个函数作为结果的函数。

    Java 8 引入了 Lambda 表达式和函数式接口,使得 Java 编程语言更加支持函数式编程。以下是一些 Java 8 之后的函数式编程例子:

    Lambda 表达式

    Lambda 表达式是一种简洁的表示匿名函数的方法,它没有名称,但有参数列表,函数主体,返回类型,并且可以被赋值给变量或传递给方法作为参数。

    例如:

    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    
    // 使用 lambda 表达式实现过滤功能
    List<Integer> evenNumbers = numbers.stream()
                                        .filter(n -> n % 2 == 0)
                                        .collect(Collectors.toList());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这个例子中,n -> n % 2 == 0 就是一个 Lambda 表达式,它实现了 Filter 接口的 test 方法。

    函数式接口

    函数式接口是只有一个抽象方法的接口。Java 8 中的 FunctionalInterface 注解可以标记这样的接口。

    例如:

    @FunctionalInterface
    interface GreetingService {
        void sayMessage(String message);
    }
    
    • 1
    • 2
    • 3
    • 4

    然后,你可以使用 Lambda 表达式或方法引用实现这个接口:

    GreetingService greetService1 = message -> System.out.println("Hello " + message);
    GreetingService greetService2 = System.out::println;
    
    • 1
    • 2

    greetService1greetService2都实现了GreetingService接口的sayMessage` 方法。

    三,省略写法

    Lambda表达式可以省略一些元素以使代码更简洁,以下是Lambda表达式的一些省略写法:

    1. 参数类型可以省略不写。如果只有一个参数,参数类型可以省略,同时()也可以省略。
    2. 如果Lambda表达式的方法体代码只有一行代码。可以省略大括号不写,同时要省略分号。
    3. 如果Lambda表达式的方法体代码只有一行代码。可以省略大括号不写。此时,如果这行代码是return语句,必须省略return不写,同时也必须省略";"。

    以上就是Lambda表达式的一些省略写法,这些规则的使用可以使你的代码看起来更加简洁易读。

    下面展示一个Java Lambda表达式的省略写法示例:

    List<String> list = Arrays.asList("Apple", "Banana", "Orange");
    
    // 原始Lambda表达式
    list.stream().filter(s -> s.startsWith("A")).forEach(System.out::println);
    
    // 省略参数类型的Lambda表达式
    list.stream().filter(s -> s.startsWith("A")).forEach(System.out::println);
    
    // 省略大括号和分号的Lambda表达式
    list.stream().filter(s -> s.startsWith("A")).forEach(s -> System.out.println(s));
    
    // 省略return语句的Lambda表达式
    list.stream().filter(s -> s.startsWith("A")).forEach(s -> System.out.println(s));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    这个示例中,创建了一个字符串列表,并使用Lambda表达式对列表进行过滤和输出。在第一个Lambda表达式中,没有省略任何内容。在第二个Lambda表达式中,省略了参数类型。在第三个Lambda表达式中,省略了大括号和分号。在第四个Lambda表达式中,省略了return语句。

    四,练习:使用Lambda 表达式 简化Comparator接口的匿名形式

    需求:定义数组并存储一些字符串,利用Arrays中的sort方法进行排序,按照字符串的长度进行排序,短的在前面,长的在后面(暂时不比较字符串里面的内容)。

    在Java中,可以使用Lambda表达式和Comparator接口来简化排序操作。以下是按照字符串长度进行排序的示例:

    import java.util.Arrays;
    import java.util.Comparator;
    
    public class Main {
        public static void main(String[] args) {
            // 定义并初始化字符串数组
            String[] strings = {"Java", "Python", "C", "JavaScript", "Go"};
            
            // 使用Lambda表达式和Comparator接口进行排序
            Arrays.sort(strings, Comparator.comparingInt(String::length));
            
            // 打印排序后的数组
            for (String str : strings) {
                System.out.println(str);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在这个示例中,Comparator.comparingInt(String::length)是一个Lambda表达式,它实现了Comparator接口的compare方法。这个表达式接受两个字符串参数,并通过调用String::length方法获取它们的长度,然后比较这两个长度值。

    这个Lambda表达式相当于下面的匿名类实现:

    Arrays.sort(strings, new Comparator<String>() {
        @Override
        public int compare(String s1, String s2) {
            return Integer.compare(s1.length(), s2.length());
        }
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    使用Lambda表达式可以简化代码,并提高代码的可读性。

    综合练习

    一,按照需求进行排序

    需求:定义数组并存储一些朋友对象,利用Arrays中的sort方法进行排序。
    属性有姓名、年龄和身高。
    按照年龄的大小进行排序,年龄一样,按照升高排序,身高一样按照姓名的字母进行排序(姓名中不要有中文或特殊字符)。

    在Java中,可以创建一个Friend类来存储朋友的姓名、年龄和身高,然后使用Arrays.sort()方法和Lambda表达式来排序。

    首先,创建一个Friend类:

    public class Friend {
        private String name;
        private int age;
        private double height;
    
        public Friend(String name, int age, double height) {
            this.name = name;
            this.age = age;
            this.height = height;
        }
    
        public String getName() {
            return name;
        }
    
        public int getAge() {
            return age;
        }
    
        public double getHeight() {
            return height;
        }
    
        @Override
        public String toString() {
            return "Friend{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", height=" + height +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    然后,你可以创建一个Friend数组并排序:

    import java.util.Arrays;
    import java.util.Comparator;
    
    public class Main {
        public static void main(String[] args) {
            Friend[] friends = new Friend[]{
                    new Friend("Alice", 25, 160.5),
                    new Friend("Bob", 20, 175.2),
                    new Friend("Charlie", 23, 170.0),
                    new Friend("David", 20, 172.0),
                    new Friend("Eve", 25, 155.0)
            };
    
            Arrays.sort(friends, Comparator.comparingInt((Friend f) -> f.getAge())
                    .thenComparingDouble(f -> f.getHeight())
                    .thenComparing(f -> f.getName()));
    
            for (Friend friend : friends) {
                System.out.println(friend);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    在这个例子中,Arrays.sort()方法使用一个自定义的比较器来排序。比较器首先比较年龄,如果年龄相同,则比较身高,如果身高也相同,则比较姓名。

    二,不死神兔

    需求:有个很有名的数学逻辑题叫做不死神兔问题,有一対兔子,从出生到第三个月起每个月都生一对兔子,小兔子长到第三个月后又生一对兔子,假设兔子都不死,问第十二个月的兔子对数为多少?

    这是一个经典的斐波那契数列问题,下面是一个的Java示例,可以计算第十二个月的兔子对数:

    public class RabbitProblem {
        public static void main(String[] args) {
            int months = 12;
            int[] rabbitPairs = new int[months];
            
            // 初始化前三个月的兔子对数
            rabbitPairs[0] = 1;
            rabbitPairs[1] = 1;
            rabbitPairs[2] = 1;
            
            // 计算后九个月的兔子对数
            for (int i = 3; i < months; i++) {
                rabbitPairs[i] = rabbitPairs[i - 1] + rabbitPairs[i - 2];
            }
            
            // 输出第十二个月的兔子对数
            System.out.println("第十二个月的兔子对数为:" + rabbitPairs[months - 1]);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    这段代码首先定义了一个长度为12的数组,用于存储每个月的兔子对数。然后,初始化前三个月的兔子对数,都是1对。接着,使用一个循环计算后九个月的兔子对数,每个月的兔子对数等于前两个月的兔子对数之和。最后,我们输出第十二个月的兔子对数。

    三,猴子吃桃

    需求:有一个猴子摘了一堆桃子,第一天吃了一半,嘴馋又多吃了一个,第二天吃剩余的一半,同样嘴馋多吃了一个,第三天亦然,第10天的时候(还没有吃),发现只剩下一个桃子了,问这个猴子摘了多少桃子?

    这是一个经典的数学问题,可以用递归或者循环来解决。这里我们给出使用循环的Java代码。

    public class MonkeyPeach {
        public static void main(String[] args) {
            int day = 10;
            int peach = 1; // 第10天有1个桃子
            for (int i = 1; i < day; i++) {
                // 每一天猴子都吃前一天剩下的一半再加一个
                peach = (peach + 1) * 2;
            }
            System.out.println("猴子摘了 " + peach + " 个桃子");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    四,爬楼梯

    需求:帅气的小明喜欢爬楼梯,他有的时候一次爬一个台阶,有的时候一次爬两个台阶。如果这个楼梯有20个台阶,小明一个共有多少种爬法?

    运行结果:
    1层台阶 1种爬法
    2层台阶 2种爬法
    7层台阶 21种爬法

    这个问题实际上是一个经典的动态规划问题,类似于斐波那契数列问题。小明爬楼梯的方式实际上就是斐波那契数列的表现形式,即F(n) = F(n-1) + F(n-2)

    以下是一段Java代码,可以计算出小明爬20层楼梯有多少种爬法:

    public class Main {
        public static void main(String[] args) {
            int n = 20;
            System.out.println(n + "层台阶 " + climbStairs(n) + "种爬法");
        }
    
        public static int climbStairs(int n) {
            if (n <= 2) {
                return n;
            }
            int[] dp = new int[n + 1];
            dp[1] = 1;
            dp[2] = 2;
            for (int i = 3; i <= n; i++) {
                dp[i] = dp[i - 1] + dp[i - 2];
            }
            return dp[n];
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这段代码中,climbStairs函数会计算出爬n层楼梯有多少种爬法。这个函数首先处理了小于等于2的情况,然后用动态规划的方式计算出了大于2的情况。动态规划的思路是,爬到第i层的方法数等于爬到第i-1层的方法数加上爬到第i-2层的方法数。最后,这个函数返回了爬到n层的方法数。

  • 相关阅读:
    键盘控制ROS车运动
    linux入门---信号的保存和捕捉
    【Linux】进程虚拟地址空间
    Hbase基本操作---idea连接hbase(shell脚本 springBoot整合Hbase)
    Poco脚本的点击位置与点击偏移
    客户案例 | 思腾合力助力深度图灵生成式AI应用平台建设
    扬帆际海:shopee跨境电商客服回复流程
    使用Unity的Input.GetAxis(““)控制物体移动、旋转
    Tomcat
    Vue - 每个页面单独设置 body 背景色(独立修改单个页面的背景色,不同页面设置不同的背景颜色)
  • 原文地址:https://blog.csdn.net/m0_62617719/article/details/132889637