JAVA算法和数据结构
一、Arrays类
1.1 Arrays基本使用
我们先认识一下Arrays是干什么用的,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));
// 2、public static 类型[] copyOfRange(类型[] arr, 起始索引, 结束索引) :拷贝数组(指定范围,包前不包后)int[] arr2 = Arrays.copyOfRange(arr, 1, 4);System.out.println(Arrays.toString(arr2));
// 3、public static copyOf(类型[] arr, int newLength):拷贝数组,可以指定新数组的长度。int[] arr3 = Arrays.copyOf(arr, 10);System.out.println(Arrays.toString(arr3));
// 4、public static setAll(double[] array, IntToDoubleFunction generator):把数组中的原数据改为新数据又存进去。double[] prices = {99.8, 128, 100};// 0 1 2// 把所有的价格都打八折,然后又存进去。Arrays.setAll(prices, new IntToDoubleFunction() {@Overridepublic double applyAsDouble(int value) {// value = 0 1 2return prices[value] * 0.8;}});System.out.println(Arrays.toString(prices));
// 5、public static void sort(类型[] arr):对数组进行排序(默认是升序排序)Arrays.sort(prices);System.out.println(Arrays.toString(prices));}
}
1.2 Arrays操作对象数组
刚才我们使用Arrays操作数组时,数组中存储存储的元素是int类型、double类型,是可以直接排序的,而且默认是升序排列。
如果数组中存储的元素类型是自定义的对象,如何排序呢?接下来,我们就学习一下Arrays如何对对象数组进行排序。
首先我们要准备一个Student类,代码如下:
public class Student implements Comparable<Student>{private String name;private double height;private int age;public Student(String name, double height, int age) {this.name = name;this.height = height;this.age = age;}
@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", height=" + height +", age=" + age +'}';}
}
然后再写一个测试类,往数组中存储4个学生对象,代码如下。此时,运行代码你会发现是会报错的。
public class ArraysTest2 {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);
// 1、public static void sort(类型[] arr):对数组进行排序。Arrays.sort(students);System.out.println(Arrays.toString(students));}
}

上面的代码为什么会报错呢?因为Arrays根本就不知道按照什么规则进行排序。为了让Arrays知道按照什么规则排序,我们有如下的两种办法。
-
排序方式1:让Student类实现Comparable接口,同时重写compareTo方法。Arrays的sort方法底层会根据compareTo方法的返回值是正数、负数、还是0来确定谁大、谁小、谁相等。代码如下:
public class Student implements Comparable<Student>{private String name;private double height;private int age;//...get、set、空参数构造方法、有参数构造方法...自己补全
// 指定比较规则// this o@Overridepublic int compareTo(Student o) {// 约定1:认为左边对象 大于 右边对象 请您返回正整数// 约定2:认为左边对象 小于 右边对象 请您返回负整数// 约定3:认为左边对象 等于 右边对象 请您一定返回0/* if(this.age > o.age){return 1;}else if(this.age < o.age){return -1;}return 0;*/
//上面的if语句,也可以简化为下面的一行代码return this.age - o.age; // 按照年龄升序排列// return o.age - this.age; // 按照年龄降序排列}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", height=" + height +", age=" + age +'}';}
}
-
排序方式2:在调用
Arrays.sort(数组,Comparator比较器);时,除了传递数组之外,传递一个Comparator比较器对象。Arrays的sort方法底层会根据Comparator比较器对象的compare方法方法的返回值是正数、负数、还是0来确定谁大、谁小、谁相等。代码如下
public class ArraysTest2 {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);
// 2、public static <T> void sort(T[] arr, Comparator<? super T> c)// 参数一:需要排序的数组// 参数二:Comparator比较器对象(用来制定对象的比较规则)Arrays.sort(students, new Comparator<Student>() {@Overridepublic int compare(Student o1, Student o2) {// 制定比较规则了:左边对象 o1 右边对象 o2// 约定1:认为左边对象 大于 右边对象 请您返回正整数// 约定2:认为左边对象 小于 右边对象 请您返回负整数// 约定3:认为左边对象 等于 右边对象 请您一定返回0
// if(o1.getHeight() > o2.getHeight()){
// return 1;
// }else if(o1.getHeight() < o2.getHeight()){
// return -1;
// }
// return 0; // 升序return Double.compare(o1.getHeight(), o2.getHeight()); // 升序// return Double.compare(o2.getHeight(), o1.getHeight()); // 降序}});System.out.println(Arrays.toString(students));}
}
二、Lambda表达式
JDK8新增的一种语法形式,叫做Lambda表达式。作用:用于简化匿名内部类代码的书写。
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(){@Overridepublic 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() {@Overridepublic double applyAsDouble(int value) {// value = 0 1 2return 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>() {@Overridepublic 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()); // 升序});System.out.println(Arrays.toString(students));}
}
2.2 Lambda表达式省略规则
Java觉得代码还不够简单,于是还提供了Lamdba表达式的几种简化写法。具体的简化规则如下
1.Lambda的标准格式(参数类型1 参数名1, 参数类型2 参数名2)->{...方法体的代码...return 返回值;}
2.在标准格式的基础上()中的参数类型可以直接省略(参数名1, 参数名2)->{...方法体的代码...return 返回值;}3.如果{}总的语句只有一条语句,则{}可以省略、return关键字、以及最后的“;”都可以省略(参数名1, 参数名2)-> 结果4.如果()里面只有一个参数,则()可以省略(参数名)->结果
接下来从匿名内部类开始、到Lambda标准格式、再到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() {@Overridepublic double applyAsDouble(int value) {// value = 0 1 2return prices[value] * 0.8;}});//2.需求:对数组中的每一个元素*0.8,使用Lambda表达式标准写法Arrays.setAll(prices, (int value) -> {return prices[value] * 0.8;});//3.使用Lambda表达式简化格式1——省略参数类型Arrays.setAll(prices, (value) -> {return prices[value] * 0.8;});//4.使用Lambda表达式简化格式2——省略()Arrays.setAll(prices, value -> {return prices[value] * 0.8;});//5.使用Lambda表达式简化格式3——省略{}Arrays.setAll(prices, value -> 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);//1.使用匿名内部类Arrays.sort(students, new Comparator<Student>() {@Overridepublic int compare(Student o1, Student o2) {return Double.compare(o1.getHeight(), o2.getHeight()); // 升序}});//2.使用Lambda表达式表达式——标准格式Arrays.sort(students, (Student o1, Student o2) -> {return Double.compare(o1.getHeight(), o2.getHeight()); // 升序});//3.使用Lambda表达式表达式——省略参数类型Arrays.sort(students, ( o1, o2) -> {return Double.compare(o1.getHeight(), o2.getHeight()); // 升序});//4.使用Lambda表达式表达式——省略{}Arrays.sort(students, ( o1, o2) -> Double.compare(o1.getHeight(), o2.getHeight()));
System.out.println(Arrays.toString(students));}
}
三、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>() {@Overridepublic int compare(Student o1, Student o2) {return o1.getAge() - o2.getAge(); // 按照年龄升序排序}});
// 使用Lambda简化后的形式Arrays.sort(students, (o1, o2) -> o1.getAge() - o2.getAge());}
}
现在,我想要把下图中Lambda表达式的方法体,用一个静态方法代替

准备另外一个类CompareByData类,用于封装Lambda表达式的方法体代码;
public class CompareByData {public static int compareByAge(Student o1, Student o2){return o1.getAge() - o2.getAge(); // 升序排序的规则}
}
现在我们就可以把Lambda表达式的方
//静态方法引用:类名::方法名
Arrays.sort(students, CompareByData::compareByAge);
法体代码,改为下面的样子
Arrays.sort(students, (o1, o2) -> CompareByData.compareByAge(o1, o2));
Java为了简化上面Lambda表达式的写法,利用方法引用可以改进为下面的样子。实际上就是用类名调用方法,但是把参数给省略了。这就是静态方法引用
3.2 实例方法引用
现在,我想要把下图中Lambda表达式的方法体,用一个实例方法代替。

在CompareByData类中,再添加一个实例方法,用于封装Lambda表达式的方法体

接下来,我们把Lambda表达式的方法体,改用对象调用方法
CompareByData compare = new CompareByData(); Arrays.sort(students, (o1, o2) -> compare.compareByAgeDesc(o1, o2)); // 降序
最后,再将Lambda表达式的方法体,直接改成方法引用写法。实际上就是用类名调用方法,但是省略的参数。这就是实例方法引用
CompareByData compare = new CompareByData(); Arrays.sort(students, compare::compareByAgeDesc); // 降序
3.2 特定类型的方法引用
Java约定:如果某个Lambda表达式里只是调用一个实例方法,并且前面参数列表中的第一个参数作为方法的主调, 后面的所有参数都是作为该实例方法的入参时,则就可以使用特定类型的方法引用。 格式:类型::方法名
public class Test2 {public static void main(String[] args) {String[] names = {"boby", "angela", "Andy" ,"dlei", "caocao", "Babo", "jack", "Cici"};// 要求忽略首字符大小写进行排序。Arrays.sort(names, new Comparator<String>() {@Overridepublic int compare(String o1, String o2) {// 制定比较规则。o1 = "Andy" o2 = "angela"return o1.compareToIgnoreCase(o2);}});//lambda表达式写法Arrays.sort(names, ( o1, o2) -> o1.compareToIgnoreCase(o2) );//特定类型的方法引用!Arrays.sort(names, String::compareToIgnoreCase);
System.out.println(Arrays.toString(names));}
}
3.3 构造器引用
现在,我们准备一个JavaBean类,Car类
public class Car {private String name;private double price;
public Car() {
}
public Car(String name, double price) {this.name = name;this.price = price;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public double getPrice() {return price;}
public void setPrice(double price) {this.price = price;}
@Overridepublic String toString() {return "Car{" +"name='" + name + '\'' +", price=" + price +'}';}
}
因为方法引用是基于Lamdba表达式简化的,所以也要按照Lamdba表达式的使用前提来用,需要一个函数式接口,接口中代码的返回值类型是Car类型
interface CreateCar{Car create(String name, double price);
}
最后,再准备一个测试类,在测试类中创建CreateCar接口的实现类对象,先用匿名内部类创建、再用Lambda表达式创建,最后改用方法引用创建。同学们只关注格式就可以,不要去想为什么(语法就是这么设计的)。
public class Test3 {public static void main(String[] args) {// 1、创建这个接口的匿名内部类对象。CreateCar cc1 = new CreateCar(){@Overridepublic Car create(String name, double price) {return new Car(name, price);}};//2、使用匿名内部类改进CreateCar cc2 = (name, price) -> new Car(name, price);
//3、使用方法引用改进:构造器引用CreateCar cc3 = Car::new;//注意:以上是创建CreateCar接口实现类对象的几种形式而已,语法一步一步简化。//4、对象调用方法Car car = cc3.create("奔驰", 49.9);System.out.println(car);}
}
四、常见算法
1.1 认识算法
接下来,我们认识一下什么是算法。算法其实是解决某个实际问题的过程和方法。比如百度地图给你规划路径,计算最优路径的过程就需要用到算法。再比如你在抖音上刷视频时,它会根据你的喜好给你推荐你喜欢看的视频,这里也需要用到算法。
我们为什么要学习算法呢?主要目的是训练我们的编程思维,还有就是面试的时候,面试官也喜欢问一下算法的问题来考察你的技术水平。最后一点,学习算法是成为一个高级程序员的必经之路。
当然我们现在并不会学习非常复杂的算法,万丈高楼平地起,我们现在只需要学习几种常见的基础算法就可以了。而且Java语言本身就内置了一些基础算法给我们使用,实际上自己也不会去写这些算法。

1.2 冒泡排序
接下来,我们学习一种算法叫排序算法,它可以价格无序的整数,排列成从小到大的形式(升序),或者从大到小的形式(降序)
排序算法有很多种,我们这里只学习比较简单的两种,一种是冒泡排序,一种是选择排序。学习算法我们先要搞清楚算法的流程,然后再去“推敲“如何写代码。(注意,我这里用的次是推敲,也就是说算法这样的代码并不是一次成型的,是需要反复修改才能写好的)。

先来学习冒泡排序,先来介绍一下,冒泡排序的流程
冒泡排序核心思路:每次将相邻的两个元素继续比较 如下图所示:第一轮比较 3次第二轮比较 2次第三轮比较 1次

public class Test1 {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.2 选择排序
刚才我们学习了冒泡排序,接下来我们学习了另一种排序方法,叫做选择排序。按照我们刚才给大家介绍的算法的学习方式。先要搞清楚算法的流程,再去推敲代码怎么写。
所以我们先分析选择排序算法的流程:选择排序的核心思路是,每一轮选定一个固定的元素,和其他的每一个元素进行比较;经过几轮比较之后,每一个元素都能比较到了。

接下来,按照选择排序的流程编写代码
ublic class Test2 {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.3 查找算法
接下来,我们学习一个查找算法叫做二分查找。在学习二分查找之前,我们先来说一下基本查找,从基本查找的弊端,我们再引入二分查找,这样我们的学习也会更加丝滑一下。
先聊一聊基本查找:假设我们要查找的元素是81,如果是基本查找的话,只能从0索引开始一个一个往后找,但是如果元素比较多,你要查找的元素比较靠后的话,这样查找的此处就比较多。性能比较差。

再讲二分查找:二分查找的主要特点是,每次查找能排除一般元素,这样效率明显提高。但是二分查找要求比较苛刻,它要求元素必须是有序的,否则不能进行二分查找。
-
二分查找的核心思路
第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.

/*** 目标:掌握二分查找算法。*/
public class Test3 {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]){// 往左边找,截止位置(右边位置) = 中间位置 - 1right = middle - 1;}else if(data > arr[middle]){// 往右边找,起始位置(左边位置) = 中间位置 + 1left = middle + 1;}else {// 中间位置处的元素值,正好等于我们要找的元素值return middle;}}return -1; // -1特殊结果,就代表没有找到数据!数组中不存在该数据!}
}
五、正则表达式
正则表达式其实是由一些特殊的符号组成的,它代表的是某种规则。
正则表达式的作用1:用来校验字符串数据是否合法
正则表达式的作用2:可以从一段文本中查找满足要求的内容

5.1 正则表达式初体验
现在,我们就以QQ号码为例,来体验一下正则表达式的用法。注意:现在仅仅只是体验而已,我们还没有讲正则表达式的具体写法。
-
不使用正则表达式,校验QQ号码代码是这样的
public static boolean checkQQ(String qq){// 1、判断qq号码是否为nullif(qq == null || qq.startsWith("0") || qq.length() < 6 || qq.length() > 20){return false;}
// 2、qq至少是不是null,不是以0开头的,满足6-20之间的长度。// 判断qq号码中是否都是数字。// qq = 2514ghd234for (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 正则表达式书写规则
这里需要用到一个方法叫matches(String regex)。这个方法时属于String类的方法。

这个方法是用来匹配一个字符串是否匹配正则表达式的规则,参数需要调用者传递一个正则表达式。但是正则表达式不能乱写,是有特定的规则的。
下面我们就学习一下,正则表达式的规则。从哪里学呢?在API中有一个类叫做Pattern,我们可以到API文档中搜索,关于正则表达式的规则,这个类都告诉我们了。

我们将这些规则,在代码中演示一下
/*** 目标:掌握正则表达式的书写规则*/
public class RegexTest2 {public static void main(String[] args) {// 1、字符类(只能匹配单个字符)System.out.println("a".matches("[abc]")); // [abc]只能匹配a、b、cSystem.out.println("e".matches("[abcd]")); // false
System.out.println("d".matches("[^abc]")); // [^abc] 不能是abcSystem.out.println("a".matches("[^abc]")); // false
System.out.println("b".matches("[a-zA-Z]")); // [a-zA-Z] 只能是a-z A-Z的字符System.out.println("2".matches("[a-zA-Z]")); // false
System.out.println("k".matches("[a-z&&[^bc]]")); // : a到z,除了b和cSystem.out.println("b".matches("[a-z&&[^bc]]")); // false
System.out.println("ab".matches("[a-zA-Z0-9]")); // false 注意:以上带 [内容] 的规则都只能用于匹配单个字符
// 2、预定义字符(只能匹配单个字符) . \d \D \s \S \w \WSystem.out.println("徐".matches(".")); // .可以匹配任意字符System.out.println("徐徐".matches(".")); // false
// \转义System.out.println("\"");// \n \tSystem.out.println("3".matches("\\d")); // \d: 0-9System.out.println("a".matches("\\d")); //false
System.out.println(" ".matches("\\s")); // \s: 代表一个空白字符System.out.println("a".matches("\s")); // false
System.out.println("a".matches("\\S")); // \S: 代表一个非空白字符System.out.println(" ".matches("\\S")); // false
System.out.println("a".matches("\\w")); // \w: [a-zA-Z_0-9]System.out.println("_".matches("\\w")); // trueSystem.out.println("徐".matches("\\w")); // false
System.out.println("徐".matches("\\W")); // [^\w]不能是a-zA-Z_0-9System.out.println("a".matches("\\W")); // false
System.out.println("23232".matches("\\d")); // false 注意:以上预定义字符都只能匹配单个字符。
// 3、数量词: ? * + {n} {n, } {n, m}System.out.println("a".matches("\\w?")); // ? 代表0次或1次System.out.println("".matches("\\w?")); // trueSystem.out.println("abc".matches("\\w?")); // false
System.out.println("abc12".matches("\\w*")); // * 代表0次或多次System.out.println("".matches("\\w*")); // trueSystem.out.println("abc12张".matches("\\w*")); // false
System.out.println("abc12".matches("\\w+")); // + 代表1次或多次System.out.println("".matches("\\w+")); // falseSystem.out.println("abc12张".matches("\\w+")); // false
System.out.println("a3c".matches("\\w{3}")); // {3} 代表要正好是n次System.out.println("abcd".matches("\\w{3}")); // falseSystem.out.println("abcd".matches("\\w{3,}")); // {3,} 代表是>=3次System.out.println("ab".matches("\\w{3,}")); // falseSystem.out.println("abcde徐".matches("\\w{3,}")); // falseSystem.out.println("abc232d".matches("\\w{3,9}")); // {3, 9} 代表是 大于等于3次,小于等于9次
// 4、其他几个常用的符号:(?i)忽略大小写 、 或:| 、 分组:()System.out.println("abc".matches("(?i)abc")); // trueSystem.out.println("ABC".matches("(?i)abc")); // trueSystem.out.println("aBc".matches("a((?i)b)c")); // trueSystem.out.println("ABc".matches("a((?i)b)c")); // false
// 需求1:要求要么是3个小写字母,要么是3个数字。System.out.println("abc".matches("[a-z]{3}|\\d{3}")); // trueSystem.out.println("ABC".matches("[a-z]{3}|\\d{3}")); // falseSystem.out.println("123".matches("[a-z]{3}|\\d{3}")); // trueSystem.out.println("A12".matches("[a-z]{3}|\\d{3}")); // false
// 需求2:必须是”我爱“开头,中间可以是至少一个”编程“,最后至少是1个”666“System.out.println("我爱编程编程666666".matches("我爱(编程)+(666)+"));System.out.println("我爱编程编程66666".matches("我爱(编程)+(666)+"));}
}
5.3 正则表达式应用案例
学习完正则表达式的规则之后,接下来我们再利用正则表达式,去校验几个实际案例。
-
正则表达式校验手机号码
/*** 目标:校验用户输入的电话、邮箱、时间是否合法。*/
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 0104644535if(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 RegexTest3 {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("您输入的邮箱格式不正确~~~");}}}
}
5.4 正则表达式信息爬取
正则表达式的第二个作用:在一段文本中查找满足要求的内容
我们还是通过一个案例给大家做演示:案例需求如下

/*** 目标:掌握使用正则表达式查找内容。*/
public class RegexTest4 {public static void main(String[] args) {method1();}
// 需求1:从以下内容中爬取出,手机,邮箱,座机、400电话等信息。public static void method1(){String data = " 来黑马程序员学习Java,\n" +" 电话:1866668888,18699997777\n" +" 或者联系邮箱:boniu@itcast.cn,\n" +" 座机电话:01036517895,010-98951256\n" +" 邮箱:bozai@itcast.cn,\n" +" 邮箱:dlei0009@163.com,\n" +" 热线电话:400-618-9090 ,400-618-4000,4006184000,4006189090";// 1、定义爬取规则String regex = "(1[3-9]\\d{9})|(0\\d{2,7}-?[1-9]\\d{4,19})|(\\w{2,}@\\w{2,20}(\\.\\w{2,10}){1,2})"+ "|(400-?\\d{3,7}-?\\d{3,7})";// 2、把正则表达式封装成一个Pattern对象Pattern pattern = Pattern.compile(regex);// 3、通过pattern对象去获取查找内容的匹配器对象。Matcher matcher = pattern.matcher(data);// 4、定义一个循环开始爬取信息while (matcher.find()){String rs = matcher.group(); // 获取到了找到的内容了。System.out.println(rs);}}
}
5.5 正则表达式搜索、替换
接下来,我们学习一下正则表达式的另外两个功能,替换、分割的功能。需要注意的是这几个功能需要用到Stirng类中的方法。

/*** 目标:掌握使用正则表达式做搜索替换,内容分割。*/
public class RegexTest5 {public static void main(String[] args) {// 1、public String replaceAll(String regex , String newStr):按照正则表达式匹配的内容进行替换// 需求1:请把下面字符串中的不是汉字的部分替换为 “-”String s1 = "古力娜扎ai8888迪丽热巴999aa5566马尔扎哈fbbfsfs42425卡尔扎巴";System.out.println(s1.replaceAll("\\w+", "-"));// 需求2(拓展):某语音系统,收到一个口吃的人说的“我我我喜欢编编编编编编编编编编编编程程程!”,需要优化成“我喜欢编程!”。String s2 = "我我我喜欢编编编编编编编编编编编编程程程";System.out.println(s2.replaceAll("(.)\\1+", "$1"));
// 2、public String[] split(String regex):按照正则表达式匹配的内容进行分割字符串,反回一个字符串数组。// 需求1:请把下面字符串中的人名取出来,使用切割来做String s3 = "古力娜扎ai8888迪丽热巴999aa5566马尔扎哈fbbfsfs42425卡尔扎巴";String[] names = s3.split("\\w+");System.out.println(Arrays.toString(names));}
}
相关文章:
JAVA算法和数据结构
一、Arrays类 1.1 Arrays基本使用 我们先认识一下Arrays是干什么用的,Arrays是操作数组的工具类,它可以很方便的对数组中的元素进行遍历、拷贝、排序等操作。 下面我们用代码来演示一下:遍历、拷贝、排序等操作。需要用到的方法如下 public…...
每日五道java面试题之spring篇(七)
目录: 第一题. 什么是Spring beans?第二题. 一个 Spring Bean 定义 包含什么?第三题. 如何给Spring 容器提供配置元数据?Spring有几种配置方式?第四题. Spring基于xml注入bean的几种方式?第五题:你怎样定义类的作用域…...
Keil编译GD32工程时找不到lib库文件
D:\Keil5\ARM\ARMCLANG\Bin\..\lib\armlib\mc_p.l:SELECTION_SCRIPT(2974): error: L6907E: Expected an expression. 问题 解决方法:因为编译器没有找到那个函数的代码,也就未解析了 其实问题很简单,把你的lib文件加进去,ok了…...
测试C#使用ViewFaceCore实现图片中的人脸遮挡
基于ViewFaceCore和DlibDotNet都能实现人脸识别,准备做个遮挡图片中人脸的程序,由于暂时不清楚DlibDotNet返回的人脸尺寸与像素的转换关系,最终决定使用ViewFaceCore实现图片中的人脸遮挡。 新建Winform项目,在Nuget包管理器中…...
2.21 Qt day2 菜单栏/工具栏/状态栏/浮动窗口、UI界面、信号与槽
思维导图 使用手动连接,将登录框中的取消按钮使用qt4版本的连接到自定义的槽函数中,在自定义的槽函数中调用关闭函数 将登录按钮使用qt5版本的连接到自定义的槽函数中,在槽函数中判断ui界面上输入的账号是否为"admin",…...
300分钟吃透分布式缓存-16讲:常用的缓存组件Redis是如何运行的?
Redis 基本原理 Redis 简介 Redis 是一款基于 ANSI C 语言编写的,BSD 许可的,日志型 key-value 存储组件,它的所有数据结构都存在内存中,可以用作缓存、数据库和消息中间件。 Redis 是 Remote dictionary server 即远程字典服务…...
上一篇文章补充:已经存在的小文件合并
对于HDFS上已经存在的大量小文件问题,有多种策略可以进行处理和优化: 1. **合并小文件**: - **使用Spark作业合并**:通过编写Spark程序读取小文件并调用repartition()或coalesce()函数重新分区数据,然后将合并后的…...
代码随想录训练营第三十期|第四十三天|动态规划 part05|1049. 最后一块石头的重量 II ● 494. 目标和 ● 474.一和零
1049. 最后一块石头的重量 II - 力扣(LeetCode) class Solution {public int lastStoneWeightII(int[] stones) {int sum 0;for (int n : stones) {sum n;}int target sum / 2;int[] dp new int[target 1];for (int i 0; i < stones.length; i…...
c++学习记录 string容器—字符串插入和删除
函数原型: string& insert(int pos,const char* s); //插入字符串string& insert(int pos,const string& str); //插入字符串string& insert(int pos,int n,char c); //在指定位置插入n个字符cstring&…...
【IEEE会议征稿】2024年第九届智能计算与信号处理国际学术会议(ICSP 2024)
2024年第九届智能计算与信号处理国际学术会议(ICSP 2024) 2024年第八届智能计算与信号处理国际学术会议(ICSP 2024)将在西安举行, 会期是2024年4月19-21日, 为期三天, 会议由西安科技大学主办。 欢迎参会&…...
【电机仿真】HFI算法脉振高频电压信号注入观测器-PMSM无感FOC控制
【电机仿真】HFI算法脉振高频电压信号注入观测器-PMSM无感FOC控制 文章目录 前言一、脉振高频电压注入法简介(注入在旋转坐标系的d轴)1.旋转高频电压(电流)注入法2.脉振高频电压注入法 二、高频注入理论1.永磁同步电机的高频模型2…...
Java学习——集合框架
Java集合框架(Java Collections Framework)是一套性能优良、使用方便的接口和类的集合,它位于java.util包下。这个框架包含了一系列集合接口的标准实现,比如列表、集合、队列,以及映射。使用这些集合,你可以…...
【鸿蒙 HarmonyOS 4.0】UIAbility、页面及组件的生命周期
一、背景 主要梳理下鸿蒙系统开发中常用的生命周期 二、UIAbility组件 UIAbility组件是一种包含UI界面的应用组件,主要用于和用户交互。 UIAbility组件是系统调度的基本单元,为应用提供绘制界面的窗口;一个UIAbility组件中可以通过多个页…...
jdk动态代理与CGLib动态代理
jdk动态代理 目标对象 package com.study;/*** 目标对象(被代理的对象)**/ public class Target implements TargetInf{public String name;public Target() {}public Target(String name) {this.name name;}public String buyCola (String name){Sys…...
Linux 命令行的世界 :4.操作文件和目录
此时此刻,我们已经准备好了做些真正的工作!这一章节将会介绍以下命令: • cp —复制文件和目录 • mv —移动/重命名文件和目录 • mkdir —创建目录 • rm —删除文件和目录 • ln —创建硬链接和符号链接 图形文件管理器能轻松地实现…...
如何使用ArcGIS Pro生成等高线
无论在制图还是规划中,经常会使用到等高线,大多数情况下,从网上获取的高程数据都是DEM文件,我们可以通过ArcGIS Pro来生成等高线,这里为大家介绍一下生成方法,希望能对你有所帮助。 数据来源 教程所使用的…...
golang学习2,golang开发配置国内镜像
go env -w GO111MODULEon go env -w GOPROXYhttps://goproxy.cn,direct...
Stable Diffusion 绘画入门教程(webui)-ControlNet(线稿约束)
上篇文章介绍了openpose,本篇文章介绍下线稿约束,关于线稿约束有好几个处理器都属于此类型,但是有一些区别。 包含: 1、Canny(硬边缘):识别线条比较多比较细,一般用于更大程度得还原照片 2、ML…...
前端笔记——var let const 之间的区别
Var: 关键字来声明变量。它有以下特点: var声明的变量作用域是函数级的,即在函数内部声明的变量在整个函数范围内可见。 var变量可以被重复声明,而不会引发错误。 var变量会存在变量提升(hoisting)现象&…...
AI工具新革命:从ChatGPT到Sora,生成式AI改变世界
这个春节着实精彩,“春山学”吃透了,不如把目光移向OpenAI又一重磅产品——文生视频大模型Sora。智能新纪元已然开启,因为正如周鸿祎所说:“,Sora的诞生意味着AGI(通用人工智能)的实现将从10年缩短到1年。”…...
python打卡day49
知识点回顾: 通道注意力模块复习空间注意力模块CBAM的定义 作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...
Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...
Cesium1.95中高性能加载1500个点
一、基本方式: 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...
关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...
Java 加密常用的各种算法及其选择
在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。 一、对称加密算法…...
NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
