前言❤️❤️
hello hello💕,这里是洋不写bug~😄,欢迎大家点赞👍👍,关注😍😍,收藏🌹🌹
上篇博客解析了很多String类的方法,例如去空格,转大小写,字符替换等等,这些方法都有个特点,那就是无法修改本身的String对象,只能创建的新的String对象去接收
这篇博客就会解析可以修改的字符串StringBulider,并解析几道比较经典String类型的基础算法题目,这些题目都要用到String类的方法,就当是方法的练习🐵
🎇个人主页:洋不写bug的博客
🎇所属专栏:Java学习之旅,从入门到进阶
🎇铁汁们对于Java的各种常用核心语法(不太常用的也有😆),都可以在上面的Java专栏学习,专栏正在持续更新中🐵🐵,有问题可以写在评论区或者私信我哦~
1,String不可修改的特性
像String这样的对象,就像常量一样,里面包含的属性是不能修改的
这里说的是堆空间中String对象里面的内容是不能变的,栈空间中的引用可以改变,引用也可以指向一个新的String对象,如下图:
可以按住ctrl键,点进去String类中看下,里面有个字节类型的数组value,这个字节数组就持有字符串的每个字节,如下图:

在String类中,并没有提供修改这个value数组的public方法,因此,如果想要修改String,通常的做法就是创造一个新的String
有的铁汁会说,那我像下面这样写,最后结果也是正常的,String对象也可以修改呀
public class Test {
public static void main(String[] args) {
String s = "hello";
for(int i = 0;i < 100;i++){
s += i;
}
System.out.println(s);
}
}
这个代码其实执行下来一共创建了100个String对象,每次都用s指向新创建的String对象,因此效率是极低的
那这种场景怎么办呢,就可以使用可变字符串StringBuilder
2,StringBuilder类简介
Java中提供了可变字符串StringBuilder,它的用法和String非常类似,可以方便的和String之间相互转化,而且字符串还可以修改
使用可变字符串的append方法进行拼接,这样就不需要每次拼接都创建新的对象,大大降低了开销
public class Test {
public static void main(String[] args) {
StringBuilder s = new StringBuilder("hello");
for(int i = 0;i < 100;i++){
s.append(i);
}
System.out.println(s);
}
}
这里测试下,for里面i加到10万,通过执行前的时间戳减去执行后的时间戳,判断程序执行的时间(单位是ms)
使用可变字符串的时间是8ms,如下:
public class Test {
public static void main(String[] args) {
long beg = System.currentTimeMillis();
StringBuilder s = new StringBuilder("hello");
for(int i = 0;i < 10_0000;i++){
s.append(i);
}
System.out.println(s);
long end = System.currentTimeMillis();
System.out.println(end - beg);
}
}

使用不可变字符串的时间是2382ms(相差接近300倍!)
public class Test {
public static void main(String[] args) {
long beg = System.currentTimeMillis();
String s = new String("hello");
for(int i = 0;i < 10_0000;i++){
s += i;
}
System.out.println(s);
long end = System.currentTimeMillis();
System.out.println(end - beg);
}
}

除了StringBullder,还有个可变字符串StringBuffer,用法和StringBuilder,只是StringBuffer加了锁,是线程安全的(这个在后面JavaEE部分的线程中会聊到),但是会牺牲效率
在绝大多数场景下,直接用StringBuilder就可以了
String类是不可变的,计算机针对不可变的东西,就可以做出一些更好的优化,提升效率:
- 能够更方便的存储到常量池中
- 针对不可变的String,可以更好的计算其哈希值(后面数据结构博客中聊哈希表的时候会解析)
- 天然线程安全(JavaEE中解析)
这里补充一个东西,铁汁们在命名引用的时候,尽量不要缩写,这样后面别人看到这个引用不知道是啥意思
下面这两种写法,一看就是第一种更好😄👍
StringBuilder stringBuilder = new StringBuilder("hello");
StringBuilder sb = new StringBuilder("hello");
3,字符串中的唯一字符
这个题目意思很好理解,那重点就是如何统计字符串中每个字符出现的个数呢
可以通过ASCII码值的转化来统计,这个方法在算法题中经常用到
如下图,ASCII码表里面的ASCII值是从0 - 127的,也就是128个数字,能表示所有的英文字符和符号
在所有算法题中,只要不特殊说明,字符串中就都是英文字符或者符号
那这个题目,我们需要统计字符串中每个字符出现的次数,那铁汁们想下能不能这样搞:
- 先创建一个长度为128的int类型的数组count,count[65]就表示ASCII码值为65的字符出现的次数(A的ASCII码值是65,count[65]就表示A在字符串中出现的次数)
- 接着利用charAt方法,从头到尾读取字符串的每个字符,那如何把字符转为ASCII码值呢?
很简单,Java中有自动的类型转化,count数组是int类型的,直接写count['A']++,这时候就会自动把'A'按照ASCII码值转化为int类型的,也就是实际效果是count[65]++ - 第二步搞完后,也就记录出了每个字符在字符串中出现的次数,这时候再从头到尾取出字符串的每个字符c,直接count[c](这里同样是自动类型转换,自动把字符类型转ASCII码值)查询下这个字符出现的次数,当发现为1时就返回
- 如果字符串中的所有字符出现的次数都大于1,那最后就返回-1
题解代码如下所示:
class Solution {
public int firstUniqChar(String s) {
int[] count = new int[128];
int n = s.length();
for(int i = 0;i < n;i++){
char c = s.charAt(i);
count[c]++;
}
for(int i = 0;i < s.length();i++){
char c = s.charAt(i);
if(count[c] == 1){
return i;
}
}
return -1;
}
}
4,字符串最后一个单词长度

这个题目直接用split按照空格进行分割,分割后会返回一个String数组,直接打印String数组最后一个元素的长度即可,非常简单
如下所示:
class Solution {
public int lengthOfLastWord(String s) {
String[] words = s.split(" ");
int n = words.length;
return words[n - 1].length();
}
}
5,验证回文串
这个题目的意思也很好理解,忽略非字母数字字符,也不看大小写,看从左往右读和从右往左读是不是一样的
首先把所有英文字母都转为小写
可以搞个双指针(在Java中也可以说是双引用),left指向最左边,right指向最右边,接着left往右走,right往左走,判断左右指向的字符是否相同
如果直到left和right重合或者left > right时,左右指向的字符都一直相等,那就是回文串
那如何去忽略那些非字母数字符号呢,可以写个isValid()方法,每次先判断下left和right引用当前指向的是不是非字母数字符号,如果是的话,就跳过
题解代码如下所示:
class Solution {
public boolean isValid(char c){
if(c >= 'a' && c <= 'z' || c >= '0' && c <= '9'){
return true;
}
return false;
}
public boolean isPalindrome(String s) {
s = s.toLowerCase();
int left = 0;
int right = s.length() - 1;
while(left < right){
while(left < right && !isValid(s.charAt(left))){
left++;
}
while(left < right && !isValid(s.charAt(right))){
right--;
}
if(s.charAt(left) != s.charAt(right)){
return false;
}
left++;
right--;
}
return true;
}
}
有两点需要注意:
- isValid()方法中,我们不需要去记忆这些字符的ASCII码值,直接用字符的形式写出来,编译器就知道是按照ASCII码值去比大小的
- 第二层whlie循环中也要加上一个left < right的判断,否则如果传入一个"!!!"字符串,那left就会一直++,直到越界
总结
这篇博客的内容量不大,Java中复习知识最好的方法,就是去搞项目或者刷算法题,在实际的使用过程中去复习,这样复习的效率是非常高的💪💪🐵
以上就是今天的所有内容啦~完结撒花~🥳🎉🎉






3597



被折叠的 条评论
为什么被折叠?



