day05-算法和数据结构

YangeIT大约 30 分钟JavaSE进阶多态final继承

day05-算法和数据结构

学习目标

各位同学,昨天我们已经把学了一部分API,今天继续来学习开发常用的API。

今天学习的内容同学们学习起来会更实在一些,了解一下 就可以了,因为实际工作用得并不多,其他的需要能理解运用即可,具体情况如下:👇

红心代表重要,梨子代表理解(重点是理解,不是运用),火箭代表了解一下,铅笔代表多练练

  1. Arrays类 🍐 ❤️ ✏️
  2. Lambda表达式 🍐 ✏️
  3. JDK8新特性 🍐✏️
  4. 常见算法 🍐✏️
  5. 正则表达式 🍐 ✏️

1.Arrays类

Arrays类

接下来我们学习的类叫做Arrays 其实Arrays并不是重点,但是我们通过Arrays这个类的学习有助于我们理解下一个知识点Lambda的学习。所以我们这里先学习Arrays,再通过Arrays来学习Lamdba这样学习会更丝滑一些!!

代码操作

1.1 Arrays基本使用

我们先认识一下Arrays是干什么用的,Arrays是操作数组的工具类,它可以很方便的对数组中的元素进行遍历、拷贝、排序等操作。

下面我们用代码来演示一下:遍历、拷贝、排序等操作。需要用到的方法如下

1667458681721
1667458681721
/**
 * 目标:掌握Arrays类的常用方法。
 */
public class ArraysTest1 {
public static void main(String[] args) {
    // 1、public static String toString(类型[] arr): 返回数组的内容
    int[] arr = {10, 20, 30, 40, 50, 60};
    System.out.println(Arrays.toString(arr));//[10, 20, 30, 40, 50, 60]

    // 2、类型[] copyOfRange(类型[] arr, 起始索引, 结束索引) :拷贝数组(指定范围,包前不包后)
    int[] arr2 = Arrays.copyOfRange(arr, 1, 4);
    System.out.println(Arrays.toString(arr2));//[20, 30, 40]

    // 3、copyOf(类型[] arr, int newLength):拷贝数组,可以指定新数组的长度。
    int[] arr3 = Arrays.copyOf(arr, 10);
    System.out.println(Arrays.toString(arr3));//[10, 20, 30, 40, 50, 60, 0, 0, 0, 0]

    // 4setAll(double[] array, IntToDoubleFunction generator):把数组中的原数据改为新数据又存进去。
    double[] prices = {99.8, 128, 100};
    //                  0     1    2
    // 把所有的价格都打八折,然后又存进去。
    Arrays.setAll(prices, new IntToDoubleFunction() {
        @Override
        public double applyAsDouble(int value) {
            // value = 0  1  2
            return prices[value] * 0.8;
        }
    });
    System.out.println(Arrays.toString(prices));//[79.84, 102.4, 80.0]

    // 5、sort(类型[] arr):对数组进行排序(默认是升序排序)
    Arrays.sort(prices);
    System.out.println(Arrays.toString(prices));//[79.84, 80.0, 102.4]
}
}

总结

课堂作业

  1. 如果数组中存储的元素类型是自定义的对象,如何排序呢? 🎤

2.Lambda表达式

接下来,我们学习一个JDK8新增的一种语法形式,叫做Lambda表达式。

Lambda表达式

Lambda表达式作用:用于简化匿名内部类代码的书写。

image
image

2.1 Lambda表达式基本使用

怎么去简化呢?Lamdba是有特有的格式的, 按照下面的格式来编写Lamdba。

(被重写方法的形参列表) -> {
    被重写方法的方法体代码;
}

要给说明一下的是,在使用Lambda表达式之前,必须先有一个接口,而且接口中只能有一个抽象方法。 (注意:不能是抽象类,只能是接口)

像这样的接口,我们称之为函数式接口 只有基于函数式接口的匿名内部类才能被Lambda表达式简化。

public interface Swimming{
    void swim();
}

有了以上的Swimming接口之后,接下来才能再演示,使用Lambda表达式,简化匿名内部类书写。

public class LambdaTest1 {
    public static void main(String[] args) {
        // 目标:认识Lambda表达式.
        //1.创建一个Swimming接口的匿名内部类对象
		Swimming s = new Swimming(){
             @Override
             public void swim() {
                 System.out.println("学生快乐的游泳~~~~");
             }
         };
         s.swim();
		
        //2.使用Lambda表达式对Swimming接口的匿名内部类进行简化
        Swimming s1 = () -> {
              System.out.println("学生快乐的游泳~~~~");
        };
        s1.swim();
    }
}

好的,我们现在已经知道Lamdba表达式可以简化基于函数式接口的匿名内部类的书写。接下来,我们可以把刚才使用Arrays方法时的代码,使用Lambda表达式简化一下了。

public class LambdaTest2 {
    public static void main(String[] args) {
        // 目标:使用Lambda简化函数式接口。
        double[] prices = {99.8, 128, 100};

        //1.把所有元素*0.8: 先用匿名内部类写法
        Arrays.setAll(prices, new IntToDoubleFunction() {
            @Override
            public double applyAsDouble(int value) {
                // value = 0  1  2
                return prices[value] * 0.8;
            }
        });
        //2.把所有元素*0.8: 改用Lamdba表达式写法
        Arrays.setAll(prices, (int value) -> {
            return prices[value] * 0.8;
        });

        System.out.println(Arrays.toString(prices));
        System.out.println("-----------------------------------------------");


        Student[] students = new Student[4];
        students[0] = new Student("蜘蛛精", 169.5, 23);
        students[1] = new Student("紫霞", 163.8, 26);
        students[2] = new Student("紫霞", 163.8, 26);
        students[3] = new Student("至尊宝", 167.5, 24);
        //3.对数组中的元素按照年龄升序排列: 先用匿名内部类写法
        Arrays.sort(students, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return Double.compare(o1.getHeight(), o2.getHeight()); // 升序
            }
        });

        //4.对数组中的元素按照年龄升序排列: 改用Lambda写法
        Arrays.sort(students, (Student o1, Student o2) -> {
            return Double.compare(o1.getHeight(), o2.getHeight()); // 升序
        });
        for (Student student : students) {
            System.out.println(student);
        }
    }
}














 
 
 



















 
 
 





好的,各位同学,恭喜大家👍👍👍👍!到这里,你已经学会了Lambda表达式的基本使用了。

image
image

总结

课堂作业

使用标准格式和简写格式都做一遍

题目:给定一个整型数组numbers,使用Lambda表达式将数组中的每个元素乘以2。 提示:可以使用Arrays.setAll方法。

Arrays.setAll(numbers,表达式);

题目:给定一个字符串数组words,使用Lambda表达式按字符串长度进行升序排序。 提示:可以使用Arrays.sort方法,并自定义比较器。

Arrays.sort(words, 表达式);

3.JDK8新特性(方法引用)

JDK8新特性(方法引用)

各位小伙伴,接下来我们学习JDK8的另一个新特性,叫做方法引用

我们知道Lambda是用来简化匿名代码的书写格式的,而方法引用 是用来进一步简化Lambda表达式的,它简化的更加过分。

到这里有小伙伴可能就想慰问Java爸爸了:“之前的代码挺好的呀!好不容易学会,你又来搞这些,把我都搞晕了。

说句大实话,确实有这样的问题,学习新的东西肯定会增加我们的学习成本,从心理上来说多少是有写抗拒的。

但是从另一个角度想,一旦我们学会了,会大大简化我们的代码书写,提高我们编写代码的效率,而且这些新的语法都是有前提条件的,遇到的时候就简化得了。

再退一步想,就算你没有学会,还是用以前的办法一点问题也没有。

给大家交代清楚了,学习方法引用可能存在的一些心理障碍,如果你是初学者你还是按照原有的写,不简化,这样可读性高一些,以后流畅了,在写这个,今天了解下。

接下来我们再正式学习方法引用的代码怎么编写。

代码操作

3.1 静态方法引用

我们先学习静态方法的引用,还是用之前Arrays代码来做演示。现在准备好下面的代码

public class Test1 {
    public static void main(String[] args) {
        Student[] students = new Student[4];
        students[0] = new Student("蜘蛛精", 169.5, 23);
        students[1] = new Student("紫霞", 163.8, 26);
        students[2] = new Student("紫霞", 163.8, 26);
        students[3] = new Student("至尊宝", 167.5, 24);

        // 原始写法:对数组中的学生对象,按照年龄升序排序
        Arrays.sort(students, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return o1.getAge() - o2.getAge(); // 按照年龄升序排序
            }
        });

        // 使用Lambda简化后的形式
        Arrays.sort(students, (o1, o2) -> o1.getAge() - o2.getAge());
    }
}

















 


现在,我想要把下图中Lambda表达式的方法体,用一个静态方法代替

1667464274934
1667464274934

准备另外一个类CompareByData类,用于封装Lambda表达式的方法体代码;

public class CompareByData {
    public static int compareByAge(Student o1, Student o2){
        return o1.getAge() - o2.getAge(); // 升序排序的规则
    }
}

现在我们就可以把Lambda表达式的方法体代码,改为下面的样子

Arrays.sort(students, (o1, o2) -> CompareByData.compareByAge(o1, o2));

Java为了简化上面Lambda表达式的写法,利用方法引用可以改进为下面的样子。实际上就是用类名调用方法,但是把参数给省略了。 这就是静态方法引用 👇 👇

//静态方法引用:类名::方法名
Arrays.sort(students, CompareByData::compareByAge);

总结

课堂作业

  1. 方法引用的作用是什么?
  2. 将静态方法引用和实例方法引用案例好好练一下即可!!!🎤

4.常见算法

4.1 认识算法

认识算法

1667402734025
1667402734025

什么是算法?

算法其实是解决某个实际问题的过程和方法。

比如百度地图给你规划路径,计算最优路径的过程就需要用到算法。

再比如你在抖音上刷视频时,它会根据你的喜好给你推荐你喜欢看的视频,这里也需要用到算法。

我们为什么要学习算法呢?

主要目的是训练我们的编程思维,

还有就是面试的时候,面试官也喜欢问一下算法的问题来考察你的技术水平。

最后一点,学习算法是成为一个高级程序员的必经之路。

当然我们现在并不会学习非常复杂的算法,万丈高楼平地起,我们现在只需要学习几种常见的基础算法就可以了。而且Java语言本身就内置了一些基础算法给我们使用,实际上自己也不会去写这些算法。

总结

课堂作业

  1. 什么是算法?为什么要学习算法🎤

4.2 冒泡排序

冒泡排序

接下来,我们学习一种算法叫排序算法 ,它可以价格无序的整数,排列成从小到大的形式(升序),或者从大到小的形式(降序)

排序算法有很多种,我们这里只学习比较简单的两种: 一种是冒泡排序,一种是选择排序。

1667403334232
1667403334232

学习算法我们先要搞清楚算法的流程,然后再去“推敲“如何写代码。 (注意,我这里用的次是推敲,也就是说算法这样的代码并不是一次成型的,是需要反复修改才能写好的,当然现在使用AI能快速生成,但是要看得懂每行代码的含义!!)。

先来学习冒泡排序,先来介绍一下,冒泡排序的流程

代码操作

冒泡排序核心思路:每次将相邻的两个元素继续比较

如下图所示:

  • 第一轮比较 3次
  • 第二轮比较 2次
  • 第三轮比较 1次
1667403618904
1667403618904
public class SortTest1 {
    public static void main(String[] args) {
        // 1、准备一个数组
        int[] arr = {5, 2, 3, 1};

        // 2、定义一个循环控制排几轮
        for (int i = 0; i < arr.length - 1; i++) {
            // i = 0  1  2           【5, 2, 3, 1】    次数
            // i = 0 第一轮            0   1   2         3
            // i = 1 第二轮            0   1             2
            // i = 2 第三轮            0                 1

            // 3、定义一个循环控制每轮比较几次。
            for (int j = 0; j < arr.length - i - 1; j++) {
                // 判断当前位置的元素值,是否大于后一个位置处的元素值,如果大则交换。
                if(arr[j] > arr[j+1]){
                    int temp = arr[j + 1];
                    arr[j + 1] = arr[j];
                    arr[j] = temp;
                }
            }
        }
        System.out.println(Arrays.toString(arr));
    }
}






 






 











总结

课堂作业

  1. 冒泡排序的核心思路是什么?简述一下🎤

4.2 选择排序

选择排序

刚才我们学习了冒泡排序,接下来我们学习了另一种排序方法,叫做选择排序 。按照我们刚才给大家介绍的算法的学习方式。

先要搞清楚算法的流程,再去推敲代码怎么写。

所以我们先分析选择排序算法的流程:选择排序的核心思路是,每一轮选定一个固定的元素,和其他的每一个元素进行比较;经过几轮比较之后,每一个元素都能比较到了。

1667467347586
1667467347586

接下来,按照选择排序的流程编写代码

代码操作

public class SortTest2 {
    public static void main(String[] args) {
        // 1、准备好一个数组
        int[] arr = {5, 1, 3, 2};
        //           0  1  2  3

        // 2、控制选择几轮
        for (int i = 0; i < arr.length - 1; i++) {
            // i = 0 第一轮    j = 1 2 3
            // i = 1 第二轮    j = 2 3
            // i = 2 第三轮    j = 3
            // 3、控制每轮选择几次。
            for (int j = i + 1; j < arr.length; j++) {
                // 判断当前位置是否大于后面位置处的元素值,若大于则交换。
                if(arr[i] > arr[j]){
                    int temp = arr[i];
                    arr[i] = arr[j];
                    arr[j] = temp;
                }
            }
        }
        System.out.println(Arrays.toString(arr));
    }
}

总结

课堂作业

  1. 选择排序的核心思路是什么?🎤

4.3 查找算法

二分查找

接下来,我们学习一个查找算法叫做二分查找

在学习二分查找之前,我们先来说一下基本查找,从基本查找的弊端,我们再引入二分查找,这样我们的学习也会更加丝滑一下。

先聊一聊基本查找:

假设我们要查找的元素是81,如果是基本查找的话,只能从0索引开始一个一个往后找,但是如果元素比较多,你要查找的元素比较靠后的话,这样查找的此处就比较多。性能比较差。

1667467611576
1667467611576

再讲二分查找

二分查找的主要特点是,每次查找能排除一般元素,这样效率明显提高。但是二分查找要求比较苛刻,它要求元素必须是有序的,否则不能进行二分查找。

  • 二分查找的核心思路
1步:先定义两个变量,分别记录开始索引(left)和结束索引(right)2步:计算中间位置的索引,mid = (left+right)/2;3步:每次查找中间mid位置的元素,和目标元素key进行比较
		如果中间位置元素比目标元素小,那就说明mid前面的元素都比目标元素小
			此时:left = mid+1
    	如果中间位置元素比目标元素大,那说明mid后面的元素都比目标元素大
    		此时:right = mid-1
		如果中间位置元素和目标元素相等,那说明mid就是我们要找的位置
			此时:把mid返回		
注意:一搬查找一次肯定是不够的,所以需要把第1步和第2步循环来做,只到left>end就结束,如果最后还没有找到目标元素,就返回-1.
1667468412882
1667468412882

代码操作

/**
 * 目标:掌握二分查找算法。
 */
public class SortTest3 {
    public static void main(String[] args) {
        // 1、准备好一个数组(如果顺序不对,需要先升序排序)
        int[] arr = {7, 23, 79, 81, 103, 127, 131, 147};

        System.out.println(binarySearch(arr, 150));

        System.out.println(Arrays.binarySearch(arr, 81));
    }

    public static int binarySearch(int[] arr, int data){
        // 1、定义两个变量,一个站在左边位置,一个站在右边位置
        int left = 0;
        int right = arr.length - 1;

        // 2、定义一个循环控制折半。
        while (left <= right){
            // 3、每次折半,都算出中间位置处的索引
            int middle = (left + right) / 2;
            // 4、判断当前要找的元素值,与中间位置处的元素值的大小情况。
            if(data < arr[middle]){
                // 往左边找,截止位置(右边位置) = 中间位置 - 1
                right = middle - 1;
                
            }else if(data > arr[middle]){
                // 往右边找,起始位置(左边位置) = 中间位置 + 1
                left = middle + 1;
            }else {
                // 中间位置处的元素值,正好等于我们要找的元素值
                return middle;
            }
        }
        return -1; // -1特殊结果,就代表没有找到数据!数组中不存在该数据!
    }
}

总结

课堂作业

  1. 为什么要学习二分查找?🎤

5. 正则表达式

5.1 正则表达式入门

正则表达式入门

接下来,我们学习一个全新的知识,叫做正则表达式。正则表达式其实是由一些特殊的符号组成的,它代表的是某种规则。

image
image

正则表达式的作用1:用来校验字符串数据是否合法

正则表达式的作用2:可以从一段文本中查找满足要求的内容

1667468712516
1667468712516

代码操作

正则表达式初体验

现在,我们就以QQ号码为例,来体验一下正则表达式的用法。

注意: 现在仅仅只是体验而已,我们还没有讲正则表达式的具体写法。

  • 不使用正则表达式,校验QQ号码代码是这样的
public static boolean checkQQ(String qq){
    // 1、判断qq号码是否为null
    if(qq == null || qq.startsWith("0") || qq.length() < 6 || qq.length() > 20){
        return false;
    }

    // 2、qq至少是不是null,不是以0开头的,满足6-20之间的长度。
    // 判断qq号码中是否都是数字。
    // qq = 2514ghd234
    for (int i = 0; i < qq.length(); i++) {
        // 根据索引提取当前位置处的字符。
        char ch = qq.charAt(i);
        // 判断ch记住的字符,如果不是数字,qq号码不合法。
        if(ch < '0' || ch > '9'){
            return false;
        }
    }
    // 3、说明qq号码肯定是合法
    return true;
    }
  • 用正则表达式代码是这样的
public static boolean checkQQ1(String qq){
    return qq != null && qq.matches("[1-9]\\d{5,19}");
}

我们发现,使用正则表达式,大大简化的了代码的写法。这个代码现在不用写,体验到正则表达式的优势就可以了。

5.2 正则表达式应用案例

正则表达式应用案例

学习完正则表达式的规则之后,接下来我们再利用正则表达式,去校验几个实际案例。 image

代码操作

  • 正则表达式校验手机号码
/**
 * 目标:校验用户输入的电话、邮箱、时间是否合法。
 */
public class RegexTest3 {
    public static void main(String[] args) {
        checkPhone();
    }

    public static void checkPhone(){
        while (true) {
            System.out.println("请您输入您的电话号码(手机|座机): ");
            Scanner sc = new Scanner(System.in);
            String phone = sc.nextLine();
            // 18676769999  010-3424242424 0104644535
            if(phone.matches("(1[3-9]\\d{9})|(0\\d{2,7}-?[1-9]\\d{4,19})")){
                System.out.println("您输入的号码格式正确~~~");
                break;
            }else {
                System.out.println("您输入的号码格式不正确~~~");
            }
        }
    }
}














 








  • 使用正则表达式校验邮箱是否正确
public class RegexTest4 {
    public static void main(String[] args) {
        checkEmail();
    }

    public static void checkEmail(){
        while (true) {
            System.out.println("请您输入您的邮箱: ");
            Scanner sc = new Scanner(System.in);
            String email = sc.nextLine();
            /**
             * dlei0009@163.com
             * 25143242@qq.com
             * itheima@itcast.com.cn
             */
            if(email.matches("\\w{2,}@\\w{2,20}(\\.\\w{2,10}){1,2}")){
                System.out.println("您输入的邮箱格式正确~~~");
                break;
            }else {
                System.out.println("您输入的邮箱格式不正确~~~");
            }
        }
    }
}

课后作业

🚩 1. 重点完成上述的课堂作业

  1. 晚自习第一节课的前30分钟,总结完毕之后,每个同学先必须梳理今日知识点 (记得写不知道的,以及感恩三件事);整理好的笔记可以发给组长,组长交给班长,意在培养大家总结的能力)

  2. 晚自习第一节课的后30分钟开始练习(记住:程序员是代码堆起来的):

    • 先要把今天的所有案例或者课堂练习,如果没练完的,练完他
    • 完成讲师发送在班级群中的作业,提交位置关注群里
    • 不懂要多问,问同学,问老师,问百度,都可以!! 独自解决分析问题,解决问题的能力-----编程思维!!
  3. 剩余的时间:预习第二天的知识,预习的时候一定要注意:

  • 预习不是学习,不要死看第二天的视频(很容易出现看了白看,为了看视频而看视频)
  • 预习看第二天的笔记,把笔记中标注重要的知识,可以找到预习视频,先看一遍,如果不懂的 ,记住做好标注。