字符串
- 创建方式 String 变量名 = “字符串内容”;
- 例1
String a = "123"; // 创建字符串
String c = "123";
System.out.println(a == c);
结果为:true 这是因为java堆内存中字符串常量池中将相字符串的地址共享,节约内存空间
- 例2
String a = "123"; // 创建字符串
String c = "123";
System.out.println(a == c);
String b = new String("123"); // new 关键字是在堆内存中开辟内存空间的意思
System.out.println(a == b); // == 对于基本数据类型来说==进行的是值的判断,对于引用数据类型来说==比较的是当前地址是否相同
int a1 = 10;
int b1 = 10;
System.out.println(a1 == b1);
在判断a==b时,结果为false,而在判断a1==b1时,结果为true,因为对于基本数据类型来说==进行的是值的判断,对于引用数据类型来说==比较的是当前地址是否相同
- 例3
equals方法
// 方法内部
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
// equals()方法首先比较的是地址是否相等,如果相等返回true,如果不相等比较的是值是否相等
// 基本数据类型只能用 == 判断,无法调用equals
当基本数据类型尝试调用equals()方法:
会产生报错,因为基本数据类型无法调用equals
常用字符串方法
- 创建字符串
String a = "Hello World!"; //创建字符串
- length( )方法,获取字符串长度
// length()方法 获取字符串长度
int length= a.length();
System.out.println(length);
输出结果:
- 获取指定字符
// charAt(index) 根据位下标获取指定的字符(下标从0开始)
char ch = a.charAt(4);
System.out.println(ch);
输出结果:
- 截取字符串
// subString(index)方法 从第index下标截取字符串(包含index),或者是个左闭右开的区间[m,n)
String b = a.substring(6);
System.out.println(b);
String c = a.substring(6,10); // [6,10)
System.out.println(c);
输出结果:
- 判断是否有子串
// contains()方法 判断是否有子串
System.out.println(a.contains("llo"));
输出结果:
- 字符串替换
//字符串替换 replace(char oldChar,char newChar)
String newa = a.replace('o','x');
System.out.println(newa);
String newb = a.replace("World","admin");
System.out.println(newb);
输出结果:
- 字符串分割
// 字符串分割 split()
String str = "apple,banana,orange";
String[] parts = str.split(","); // 以","分割str字符串
System.out.println(Arrays.toString(parts));
for (int i = 0;i<parts.length;i++){
System.out.println(parts[i]);
输出结果:
KMP算法
在大串中找到小串
-
例子:现在要在aabaabaafa字符串中找到aabaaf
传统解法:利用双层for循环
-
两个游标从前向后进行遍历,并且比较值是否相等,如果相等执行i++,j++
-
如果不相等,则i游标回到下标为1的位置,j游标归0从新开始
这种算法时间复杂度较高O(n2)
KMP算法
-
两个游标从前向后进行遍历,并且比较值是否相等,如果相等则i++,j++;如果不相等,那么转到步骤二
-
让游标i不动,游标j指向b
但是如何将下标指向b?
答:需要找到最长相等的前后缀长度,要用到前缀表(常用的是next表)
对于字符串:aabaaf
前缀:不包含最后一个字符的所有以第一个字符开头的连续子串
a、aa、aab、aaba、aabaa
后缀:不包含第一个字符的所有以最后一个字符结尾的连续子串
f、af、aaf、baaf、abaaf
此时找到最长相等的前后缀长度过程如下:
a 只有a,没有前后缀
aa 前缀为a,后缀为a,最长相等的前后缀长度为1
aab 前缀:a、aa,后缀:b、ab,最长相等的前后缀长度为0
aaba 前缀:a、aa,aab后缀:a、ba,aba最长相等的前后缀长度为1
aabaa 前缀:a、aa,aab、aaba后缀:a、aa,baa、abaa,最长相等的前后缀长度为2
aabaaf 前缀:a、aa,aab、aaba、aabaa后缀:f、af、aaf、baaf、abaaf,最长相等的前后缀长度为0
所以构建出来的前缀表为:
前缀表 0 1 0 1 2 0 数组 a a b a a f 然后就可以根据前缀表,找到b所在的位置,将游标j定位
-
前缀表和next数组
很多KMP算法的实现都是使用next数组来做回退操作,那么next数组与前缀表有什么关系呢? next数组就可以是前缀表,但是很多实现都是把前缀表统一减一(右移一位,初始位置为-1)之后作为next数组。如下图
-
-
字符串拼接
反编译javap -c
例1
String s1 = "123";
String s2 = "456";
String s3 = s1+s2; // 编译期间调用了stringBuffer里面的append()方法 其中为s3开辟了新的内存地址
System.out.println(s3);
String s4 = "123456";
System.out.println(s3 == s4); // 结果为false
正常的话s3的值为字符串123456,而s4的值也是字符串“123456”,java中相同字符串本应该共享字符串常量池,s3与s4指向的地址应该一样,但是最终判断结果并不为true,是因为String s3 = s1+s2; 编译期间调用了stringBuffer里面的append()方法 其中为s3开辟了新的内存地址,导致s3与s4指向的地址并不一样。
例2
String s1 = "123" + "456"; // 编译期间直接进行自动拼接
String s2 = "123456";
System.out.println(s1 == s2); // 结果为true
而上面这段代码中,s1 = “123” + “456”,在编译过程中并没有调用方法开辟新的内存空间,而是直接将"123"、"456"两个字符串拼接并赋值给了s1,所以在最终判断时结果为true