09_字符串
一 字符串简介
字符串:是由若干个字符组成的一个有序的序列。在Java中,使用String这个类来描述字符串。
- String位于java.lang包下,不需要import,直接使用。
- String类型使用了final修饰词,因此不能被继承(不能有子类型)
- 字符串底层封装的是字符数组及其针对数组的操作方法
- 字符串一旦创建,对象永远无法改变,但字符串引用可以重新赋值
- 字符串在内存中采用Unicode编码方式,任何一个字符对应两个字节的定长编码
- 字符串的索引值从0开始
二 字符串常量池
JVM为了提升性能和减少内存开销,避免重复创建字符串,其维护了一块特殊的内存空间,即字符串常量池。当需要使用静态字符串(字面量/常量/常量连接的结果)时,先去检查字符串常量池是否存在该字符串,若存在,则直接返回该字符串的引用地址;若不存在,则在字符串常量池中创建字符串对象,并返回对象的引用地址。
其内存位置:
- 在 JDK 1.7 之前,运行时常量池(包括字符串常量池)存放在方法区。
- 在 JDK 1.7 时,字符串常量池被从方法区转移至 Java 堆中,注意并不是运行时常量池,而是字符串常量池被单独转移到堆,运行时常量池剩下的东西还是方法区中。
- 在 JDK 1.8 时,此时字符串常量池还在堆中。
案例演示:
/**
* 字符串的拼接以及常量池
* 1. 字符串常量池:
* jvm为了提升性能和减少内存开销,专门为字符串的一些操作,在内存中专门提供了
* 一块区域,用于存储字符串对象。该内存区域就是字符串常量池(字符串缓冲区,缓冲池)
*
* 什么时候会用到字符串常量池?
* 当使用字面量给变量赋值时,会先去常量池中寻找是否该字面量的对象,如果有,就将其地址直接
* 给变量。如果没有,就将该字面量对象创建在常量池中,然后将其地址赋值给变量。
*/
public class StringDemo01 {
public static void main(String[] args) {
String s1 = "abc"; //
String s2 = "abc"; //
//判断s1和s2的对象地址是不是同一个
System.out.println(s1 == s2); //true
/*
* 两个字面量做拼接操作时,编译器在编译期间就做了优化操作
* 直接计算出结果,即s3="abcd"
*/
String s3 = "abc"+"d";
String s4 = "abcd";
System.out.println(s3 == s4); //true
String s5 = "d";
// 拼接操作只要有变量参与,那么javac不会进行优化。只能在运行期间进行计算,
// 计算后的结果,存储在堆中,并不是常量池里
String s6 = "abc"+s5;
System.out.println(s6 == s4); //false
String s7 = "ab"+"c"+s5; // 优化成了"abc"+s5. 但是依然在运行期间与s5做的计算. 不会去堆里找,是新对象
System.out.println(s7 == s6); // false
String s8 = new String("d");
String s9 = "abc"+s8;
System.out.println(s7 == s9);//false
String s10 = new String("abc");
String s11 = new String("abc");
System.out.println(s10 == s11);//false
//假如只有下面三行代码:问 内存中总共有多少个对象 5个对象
String s12 = "h";
String s14 = "abc"+s12+"o";
String s13 = new String(s14);
}
}
三 常用构造器
| 序号 | 构造器 | 解析 |
|---|---|---|
| 1 | String() | 初始化一个新创建的空字符序列的字符串对象 |
| 2 | String(String str) | 初始化一个新创建的字符串对象,使其字符序列与参数相同。换句话说,新创建的对象是该参数的副本 |
| 3 | String(byte[] bytes) | 使用默认编码集解码byte数组,构建一个字符串对象 |
| 4 | String(byte[] bytes,String charsetName) | 使用指定编码集charsetName 解码byte数组,构建一个字符串对象 |
| 5 | String(byte[] bytes, int offset, int length) | 使用默认编码集解码byte数组的从offset开始,length个元素,构建一个字符串对象 |
| 6 | String(char[] value) | 初始化一个字符串对象,使其字符序列包含参数的元素 |
四 常用方法
| 1 | String concat(String str) | 将this字符串与字符串str进行拼接,返回一个新的字符串对象。 相当于 两个字符串使用+号做拼接操作 |
|---|---|---|
| 2 | static String join(String delimiter,String... elements) | 将指定的所有字符串元素使用delimiter字符连接起来,返回一个新的字符串 |
| 3 | int length() | 返回字符串中的字符个数。 注意与数组的length区分开。 |
| 4 | char charAt(int index) | 返回指定索引处的char字符 |
| 5 | int indexOf(String subStr) | 返回指定字符串在此字符串中第一次出现的索引,找不到返回-1 |
int indexOf(String str,int fromIndex) | 从指定位置fromIndex开始向后检索,返回指定字符串在此字符串中第一次出现的索引,找不到返回-1 | |
| 6 | int lastIndexOf(String str) | 用于返回指定字符串在此字符串中最后一次出现的索引,找不到返回-1 |
int lastIndexOf(String str,int endIndex) | 从开始检索到指定位置endIndex,用于返回指定字符串在此字符串中最后一次出现的索引,找不到返回-1 | |
| 7 | String toUpperCase() | 将字符串中的所有字母转换成大写 |
| 8 | String toLowerCase() | 将字符串中的所有字母转换成小写 |
| 9 | char[] toCharArray() | 将字符串转变成字符数组 |
| 10 | boolean startsWith(String str) | 检查一个字符是否以指定的字符串为前缀 |
| 11 | boolean endsWith(String str) | 检查一个字符是否以指定的字符串为后缀 |
| 12 | String substring(int beginIndex) | 从指定位置beginIndex开始截取,截取到最后一个字符,并返回 |
String substring(int beginIndex,int endIndex) | 从指定位置beginIndex开始截取,截取到指定位置endIndex,并返回,包前不包后。 | |
| 13 | String trim() | 去掉一个字符串的前与后的空字符,不会去掉中间的空字符 |
| 14 | String replace(String target,String replacement) | 使用replacement字符串替换掉target字符串 |
| 15 | char[] getBytes() | |
char[] getBytes(String charset) | ||
| 16 | boolean isEmpty() | |
| 17 | boolean contains(String subStr) | |
| 18 | int compareTo(String str) | |
int compareToIgnoreCase(String str) | ||
| 19 | static String valueOf(int value) | 将其他类型转换成字符串类型 |
static String valueOf(double d) | ||
static String valueOf(char[] ch) | ||
static String valueOf(Object obj) | ||
boolean matches(String regex) | ||
String[] split(String regex) | ||
replaceAll(String regex, String replacement) | ||
replaceFirst(String regex, String replacement) |
案例演示:
publc static void main(String[] args){
String str1 = "hello";
String str2 = "world";
// 1、将两个字符串拼接到一起,这个过程中,会new一个新的字符串对象,并返回结果
String ret1 = str1 + str2;
// 2、将两个字符串拼接到一起,这个过程中,会new一个新的字符串对象,并返回结果
String ret2 = str1.concat(str2);
// 3、从指定的下标位开始截取字符串,一直到最后一位,此时,结果为 "llo"
String ret3 = str1.substring(2);
// 4、截取一个字符串的指定范围 [fromIndex, endIndex),此时,结果为 "ll"
String ret4 = str1.substring(2, 4);
// 5、截取一个字符序列的指定范围 [fromIndex, endIndex),返回值CharSequence是一个接口
CharSequence ret5 = str1.subSequence(2, 4);
// 6、将一个字符串中的指定字符替换成新的字符
String ret6 = str1.replace( 'l', 'L');
// 7、将一个字符串中的指定部分替换成新的部分
String ret7 = str1.replace("he", "AAAAAAA");
// 8、将一个字符串转成字符数组
char[] array1 = str1.toCharArray();
// 9、将一个字符串转成一个字节数组,以项目默认的字符集进行转换
byte[] array2 = str1.getBytes();
// 10、将一个字符串转成一个指定字符集的字节数组
try {
byte[] array3 = str1.getBytes("utf8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
// 11、查询下标
// 11.1、查询某一个字符在一个字符串中第一次出现的下标
int index1 = str1.indexOf( 'l');
// 11.2、查询某一个字符在一个字符串中最后一次出现的下标
int index2 = str1.lastIndexOf( 'l');
// 11.3、查询某一个字符在一个字符串中从指定的位置开始往后第一次出现的下标
int index3 = str1.indexOf( 'l', 3);
// 11.4、查询某一个字符在一个字符串中从指定的位置开始最后一次出现的下标 // 从index位开始向前遍历,第一个指定字符出现的下标
int index4 = "hellollo".lastIndexOf( 'l', 4); // hello
/*
* 以下四个查询下标的方法,和上方查询字符下标的逻辑是一模一样的
* 查询到的下标是字符串中的第0个字符的下标
*
* indexOf(String str)
* indexOf(String str, int fromIndex)
* lastIndexOf(String str)
* lastIndexOf(String str, int fromIndex)
*/
// 12、将一个字符串中的小写字母部分转成大写字母
String ret8 = str1.toUpperCase();
// 13、将一个字符串中的大写字母部分转成小写字母
String ret9 = str1.toLowerCase();
// 14、获取一个字符串的长度
int length = str1.length();
// 15、判断一个字符串是否是空字符串
boolean ret10 = str1.isEmpty();
// 16、判断一个字符串中是否包含指定的子字符串
boolean ret11 = str1.contains("ll");
// 17、判断字符串的前缀
str1.startsWith("he");
// 18、判断字符串的后缀
str1.endsWith("lo");
// 19、去除一个字符串首尾的空格
String ret12 = " hello world ".trim();
// 20、比较两个字符串
/*
* 两个字符串的比较规则 :
* 1、依次比较从第0个字符开始的每一个字符
* 2、如果某一次的比较可以分出大小,则比较结束,以这个字符的大小为最终结果
*
* 比较结果 :
* > 0 : 前面的字符串大于后面的字符串
* = 0 : 前面的字符串等于后面的字符串
* < 0 : 前面的字符串小于后面的字符串 */
int ret13 = "hello".compareTo("he");
// 21、比较两个字符串,忽略大小写
int ret14 = "hello".compareToIgnoreCase("HELLO");
// 22、切割字符串
String[] ret15 = "hello world nihao shijie xiaoming".split(" ");
// 23、拼接
String ret = String.join(", ", "xiaoming", "xiaobai", "xiaohei", "xiaolv");
System.out.println(ret);
}
五 StringBuilder和StringBuffer
由于字符串是不可变的,因此对字符串进行操作的时候,比较的麻烦,每次的操作都需要实例化一个 临时的空间来存放结果,效率不高。可以使用其他的某些类,来实现高效率的字符串的操作:StringBuilder和StringBuffer。
StringBuilder和StringBuffer,都是一个可变的字符序列(即可变的字符串),对象创建后可以通过调用方法来实现字符序列的改变,StringBuilder和StringBuffer类中的方法完全相同。 只需要学会其中一个,另一个就会了。
5.1 常用构造器
StringBuilder():构造一个不带任何字符的字符串生成器,其初始容量为16个字符StringBuilder(String str):构造一个字符串生成器,其初始化为指定的字符串内容
5.2 常用方法
StringBuilder的很多方法的返回值均为StringBuilder类型,这些方法的返回值语句是:return this。也就是返回对象本身(地址),因此可以使用流式编程。
| 序号 | 常用方法 | 作用 | 备注 |
|---|---|---|---|
StringBuilder append(String str) | 将指定字符串追加到this字符串后面,返回this | 有很多重载方法 | |
StringBuilder insert(int index,String str) | 将指定字符串插入到this字符串的指定索引处,返回this | 有很多重载方法 | |
StringBuilder delete(int start, int end) | 删除字符串中的一部分 | ||
StringBuilder reverse() | 将字符序列进行反转(左右颠倒) | ||
String toString() | 将StringBuilder对象转变成String对象 |
案例演示:
// 对StringBuffer和StringBuilder进行的操作,可以直接影响自身,不用接受返回值。 // 1、构造,空字符串
// StringBuffer sb = new StringBuffer(); // 2、通过一个字符串实例化一个StringBuffer对象
StringBuffer sb = new StringBuffer("hello");
// 3、向后拼接元素
sb.append("!");
// 4、向指定位插入元素
sb.insert(5, " world");
// 5、删除指定范围的元素 [from, end)
sb.delete(6, 11);
// 6、删除指定下标的元素
sb.deleteCharAt(5);
// 7、将一个字符串的指定部分替换成新的字符串 hello!
sb.replace(5, 6, " world");
// 8、字符串翻转 "上海自来水来自海上 "
sb.reverse();
// 9、转成字符串
sb.toString();
5.3 三者的区别
- StringBuilder/StringBuffer是可变字符串,如果进行字符串的内容计算,建议选择StringBuilder,这样性能更好一些。String是不可变字符串。
- java的字符串拼接的过程底层就是利用StringBuilder实现的
- StringBuilder/StringBuffer 也是final修饰的类型,不能被继承
- StringBuilder/StringBuffer没有重写equals方法,String重写了equals方法
- StringBuilder是非线程安全的,并发处理,性能稍快
- StringBuffer是线程安全的,同步处理,性能稍慢
六 练习题以及八股文
- 以下代码输出什么?为什么?
String s1 = "hello";
String s2 = "hello";
System.out.println(s1 == s2);
//输出true,s1在创建时检查字符串常量池不存在“hello”,在字符串常量池中创建字符串对象,并返回对象的引用地址
//第二次创建s2时,存在“hello”,则直接返回该字符串的引用地址,s1和s2指向相同的地址,返回true
2.String、StringBuilder 和 StringBuffer 有什么区别?
1.String 是 Java 中的一个类,用于表示字符串。它是不可变的,即一旦创建就不能被修改。
2.StringBuffer 和 StringBuilder 也是 Java 中的类,用于表示可变的字符串。它们可以进行插入、删除和替换等操作。
- java的字符串拼接的过程底层就是利用StringBuilder实现的
3.他们都是被final修饰的类,不能被继承
4. StringBuilder/StringBuffer没有重写equals方法,String重写了equals方法
5. StringBuilder是非线程安全的,并发处理,性能稍快
6. StringBuffer是线程安全的,同步处理,性能稍慢
3.解释字符串常量池(String Pool)的概念及其作用。
字符串常量池 (String Constant Pool) 是 Java 中一种用于优化内存和提高性能的机制。它是方法区 (Java 8
之前) 或元空间 (Java 8 之后) 中专门用来存储 不可变字符串常量
的区域。当使用字符串字面量创建字符串时,JVM会首先检查字符串常量池中是否已经存在该字符串。如果存在,则直接引用;如果不存在,则在池中创建一个新的字符串对象。
作用: 避免在内存中存储大量内容相同的字符串,减少内存浪费,提高效率。
4.以下代码创建了几个 String 对象?
String s1 = new String("hello");
String s2 = "hello";
//2个
5.编写一个方法,判断一个字符串是否为回文串(忽略大小写和空格)。
public static boolean isPalindromeString(String str){
if(str == null) {
return false;
}
StringBuilder sb = new StringBuilder(str);
return sb.reverse().toString().equals(str);
}
5.如何反转一个字符串?请提供至少两种实现方式。
//创建一个StringBuilder对象,调用其中的reverse()
StringBuilder sb = new StringBuilder(str);
return sb.reverse().toString();
//将字符串转变位字符数组,利用循环交换元素位置,在将其转为字符串
public static String reverse(String str){
char[] chars = str.toCharArray();
int left = 0;int right = chars.length - 1;
while(left < right){
char temp = chars[left];
chars[left] = chars[right];
chars[right] = temp;
left++;
right--;
}
return new String(chars);
}
7.实现一个方法,统计字符串中每个字符出现的次数。
//使用Hash表HashMap<Character, Integer>
Map<Character, Integer> map = new HashMap<>();
for (char c : str.toCharArray()) {
if (map.containsKey(c)) {
map.put(c, map.get(c) + 1); // 已存在,次数+1
} else {
map.put(c, 1); // 首次出现,次数设为1
}
}
//使用数组当哈希桶
public static void count(String str){
int[] count = new int[128];
for(char c : str.toCharArray()){
count[c]++;
}
for (int i = 0; i < 128; i++) {
if(count[i] != 0){
System.out.println((char)i + " " + count[i]);
}
}
}
//先排序,再扫一遍,相邻相同的字符就是同一组的计数
public static void countChar(String str) {
if (str == null || str.isEmpty()) return;
// 1. 转数组并排序
char[] a = str.toCharArray();
Arrays.sort(a);
// 2. 线性扫描
char cur = a[0];
int cnt = 1;
for (int i = 1; i < a.length; i++) {
if (a[i] == cur) {
cnt++; // 相同字符,计数+1
} else {
System.out.println(cur + " : " + cnt); // 输出上一段结果
cur = a[i]; // 切换到新字符
cnt = 1;
}
}
System.out.println(cur + " : " + cnt); // 输出最后一段
}
8.写一个方法,将字符串中的单词顺序颠倒(例如:“hello world” → “world hello”)。
//切割单词
public static String split(String str){
String[] words = str.split(" ",2);
return words[1]+" "+words[0];
}
//使用substring()截取
9.如何在字符串中查找所有匹配的子串位置?
public static List<Integer> found(String parent, String child) {
List<Integer> indices = new ArrayList<>();
if (child.isEmpty()) return indices; // 空子串直接返回
for (int idx = parent.indexOf(child);
idx >= 0;
idx = parent.indexOf(child, idx + 1)) {
indices.add(idx);
}
return indices; // 返回所有起始索引
}
List<Integer> found = found("asdfghjklasdasdasd", "as");
System.out.println(found);
//found("dfghjklasdasdasd", "as");
10.实现字符串的压缩功能,如 “aaabbc” 压缩为 “a3b2c1”。
//字符串压缩功能
public static String compress(String s) {
if(s == null ||s.isEmpty())return "";
StringBuilder sb = new StringBuilder();
int count =1;
char crrent = s.charAt(0);
for (int i = 1; i < s.length(); i++) {
if( s.charAt(i) ==crrent ){
count++;
}else{
sb.append(crrent);
if (count > 1) sb.append(count);
crrent = s.charAt(i);
count = 1;
}
}
//处理最后一组字符
sb.append(crrent);
if(count>1) sb.append(count);
return sb.length() < s.length() ? sb.toString() : s;
}
11.equals() 和 equalsIgnoreCase() 方法有什么区别?
equals方法是用于比较两个字符串是否相等,它区分大小写。具体来说,如果两个字符串的字符序列完全相同且大小写也完全相同,则返回true;否则返回false。而equalsIgnoreCase()会忽略字符串中字符的大小写,只要字符序列相同就返回true;否则返回false。
12.如何比较两个字符串的大小?有哪些方法?
使用 == 运算符:比较地址值
equals 方法:将逐个地比较两个字符串的每个字符是否相同。如果两个字符串具有相同的字符和长度,它返回 true,否则返回 false。检查大小写
compare 方法:用于按字典顺序比较两个字符串的大小,该比较是基于字符串各个字符的 Unicode 值
13.写一个方法将字符串中的驼峰命名转换为下划线命名(如:“userName” → “user_name”)。
public static String camelToSnake(String camel) {
if (camel == null || camel.isEmpty()) return camel;
StringBuilder sb = new StringBuilder();
char[] chars = camel.toCharArray();
for (int i = 0; i < chars.length; i++) {
char c = chars[i];
// 当前字符是大写,并且不是首字符
if (Character.isUpperCase(c) && i > 0) {
sb.append('_');
}
// 统一转小写后追加
sb.append(Character.toLowerCase(c));
}
return sb.toString();
}
14.如何将字符串转换为基本数据类型(int, double等)?
1.使用包装类的静态方法,如 Integer.parseInt(str); Long.parseLong(str); Double.parseDouble(str); Float.parseFloat(str);
2.包装类的 valueOf 方法:返回包装类对象,再自动拆箱(效果一样,但多一步装箱)
15.解释 String 的 intern() 方法的作用和使用场景。
intern 方法是 String 类中的一个 native 方法,用于将字符串对象放入字符串常量池,并返回唯一实例。
原理: 检查常量池:当调用 intern 方法时,JVM 首先检查字符串常量池中是否已经存在内容相同的字符串。
返回引用:如果常量池中存在相同内容的字符串,则返回常量池中该字符串的引用。
添加到常量池:如果常量池中不存在相同内容的字符串,则将当前字符串对象添加到常量池,并返回其引用。
使用场景:
1.大规模去重 读文件、网络报文、字典等出现大量重复字符串时,用 intern() 保证只存一份,显著减少内存。
2.Map 的 Key 优化 把作为 HashMap/HashSet key 的字符串先 intern(),可减少相同内容不同对象带来的额外存储。
3.== 比较需求 对高频、内容有限的字符串(如枚举、状态码)做 intern() 后可直接用 == 代替 equals(),提升速度。
16.什么是不可变对象?为什么 String 要设计成不可变的?
不可变对象
-类本身被声明为 final,防止被继承后破坏语义。
-所有字段都是 private final,且没有 setter / 任何修改方法。
-如果字段是引用类型,也要保证该引用指向的对象同样不可变或不被外部修改。
-对象一旦创建,其内部状态(所有字段值)就永远无法被改变。
为什么String 要设计成不可变的
-1、常量池的需要 字符串常量池(String pool)是 Java 堆内存中一个特殊的存储区域,当创建一个 String 对象时,假如此字符串已经存在于常量池中,则不会创建新的对象,而是直接引用已经存在的对象。这样做能够减少 JVM 的内存开销,提高效率。
-2.hashcode 缓存的需要 因为字符串不可变,所以在它创建的时候 hashcode 就被缓存了,不需要重新计算。这就使得字符串很适合作为 HashMap 中的 key,效率大大提高
-3.多线程安全 多线程中,可变对象的值很可能被其他线程改变,造成不可预期的结果。而不可变的 String 可以自由在多个线程之间共享,不需要同步处理
拔高题:
17.写一个方法,找出字符串中第一个不重复的字符。
//写一个方法,找出字符串中第一个不重复的字符。
public static char firstUniqueChar(String s) {
if (s == null || s.isEmpty()) return '\0'; // 约定:无结果返回 '\0'
int[] cnt = new int[256]; // ASCII 共 256 码位
char[] chars = s.toCharArray();
// 1. 计数
for (char c : chars) cnt[c]++;
// 2. 按原顺序找第一个出现次数为 1 的字符
for (char c : chars) {
if (cnt[c] == 1) return c;
}
return '\0'; // 没有找到
}
//其他实现
public static String noRepeat(String str){
for (int i = 0; i < str.length(); ) {
if (str.indexOf(str.charAt(i),i+1)==-1){
return String.valueOf(str.charAt(i));
}
// System.out.println(String.valueOf(str.charAt(i)));
String regex=String.valueOf(str.charAt(i));
str=str.replaceAll(regex,"");
}
return "nothing haven't repeat";
}
public static void main(String[] args) {
System.out.println(noRepeat("f8f[dgddsvdsddscx"));
}
}
18.如何实现字符串的模糊匹配(支持通配符 * 和 ?)?
public static boolean isMatch(String text, String pattern) {
int i = 0, j = 0, starIdx = -1, match = 0;
while (i < text.length()) {
if (j < pattern.length() && (pattern.charAt(j) == '?' || pattern.charAt(j) == text.charAt(i))) {
i++; j++; // 匹配或 '?'
} else if (j < pattern.length() && pattern.charAt(j) == '*') {
starIdx = j++; // 记录 '*' 位置
match = i; // 记录文本位置
} else if (starIdx != -1) { // 回溯
j = starIdx + 1;
i = ++match;
} else {
return false;
}
}
// 跳过末尾所有 '*'
while (j < pattern.length() && pattern.charAt(j) == '*') j++;
return j == pattern.length();
}
/**
* 字符串模糊匹配函数,支持通配符*和?
* * 匹配任意数量(包括0个)的任意字符
* ? 匹配恰好一个任意字符
*
* @param pattern 带通配符的模式串
* @param text 待匹配的文本串
* @return 匹配成功返回true,否则返回false
*/
public static boolean fuzzyMatch(String pattern, String text) {
// 预处理:将连续的*压缩为单个*
StringBuilder processedPattern = new StringBuilder();
for (int i = 0; i < pattern.length(); i++) {
char c = pattern.charAt(i);
if (c == '*') {
// 只添加第一个*,跳过后续连续的*
if (processedPattern.length() == 0 || processedPattern.charAt(processedPattern.length() - 1) != '*') {
processedPattern.append(c);
}
} else {
processedPattern.append(c);
}
}
int i = 0; // text字符串的指针
int j = 0; // pattern字符串的指针
int starIndex = -1; // 记录最后一个*的位置
int matchIndex = 0; // 记录*匹配到的text位置
int textLen = text.length();
int patternLen = processedPattern.length();
while (i < textLen) {
// 情况1:当前字符匹配(普通字符相等或模式为?)
if (j < patternLen && (processedPattern.charAt(j) == text.charAt(i) || processedPattern.charAt(j) == '?')) {
i++;
j++;
}
// 情况2:遇到*,记录位置并假设*匹配0个字符
else if (j < patternLen && processedPattern.charAt(j) == '*') {
starIndex = j;
matchIndex = i;
j++;
}
// 情况3:之前有*,回溯让*匹配更多字符
else if (starIndex != -1) {
matchIndex++; // *多匹配一个字符
i = matchIndex;
j = starIndex + 1; // 回到*后面的字符继续匹配
}
// 情况4:无匹配且无*可回溯,匹配失败
else {
return false;
}
}
// 处理模式串剩余部分(必须全为*才匹配成功)
while (j < patternLen && processedPattern.charAt(j) == '*') {
j++;
}
// 模式串完全匹配完毕才成功
return j == patternLen;
}
19.分析以下代码的性能问题,并提供优化方案:
String result = "";
for (int i = 0; i < 10000; i++) {
result += "a";
}
//在字符串常量池里创建了多个无用的对象,不断存取,消耗了大量的资源
//使用StringBuilder完成
StringBuilder sb = new StringBuilder("");
for (int i = 0; i < 10000; i++) {
sb.append("a");
}
更多推荐


所有评论(0)