String 类下篇
前言: 上篇我们学习了 Stirng类的一些常用方法, 外加初步了解了一下我们的字符串常量池, 本文 继上文,完成String类的剩下知识点 。
1.字符串的不可变性
相比大家都 听过, 字符串 是 不可变 的 , 那么String类 是为什么是不可变的呢?
在我们现在的 知识储备上我们可以 认为 String 是不可改变的,但实际上我们 可以通过反射 进行 改变,反射将在后面学到,所以我们现在就认为 String类是不可改变的。
既然我们知道了 字符串是不可改变的,那么我们要如何 去完成字符串的修改呢?
字符串的修改
这里先来看一段代码
这里我们就会产生 3 个对象 (注意: 这里没有算 String 类 中的 value 数组)
刚刚我们说过 我们的字符串是 不可 变的 ,我们并不能在原来的字符串上 进行修改, 只能重新生成 对象, 所以我们 要 尽量避免直接对String类型对象进行修改,因为String类是不能修改的,所有的修改都会创建新对象,效率非常低下。
针对 说 : String类是不能修改的,所有的修改都会创建新对象,效率非常低下
我们可 通过 代码来验证,这里 会涉及到 StringBuilder, StringBufferu , 正好将我们 接下要介绍的 这个两个 给 引出来
public static void main(String[] args) {
// 通过 System 底下的方法计算 时间戳
long start = System.currentTimeMillis();
String s = "";
for (int i = 0; i < 10000; ++i) {
s += i;
}
long end = System.currentTimeMillis();
System.out.println(end - start);
start = System.currentTimeMillis();
StringBuffer sbf = new StringBuffer("");
for (int i = 0; i < 10000; ++i) {
sbf.append(i);
}
end = System.currentTimeMillis();
System.out.println(end - start);
start = System.currentTimeMillis();
StringBuilder sbd = new StringBuilder();
for (int i = 0; i < 10000; ++i) {
sbd.append(i);
}
end = System.currentTimeMillis();
System.out.println(end - start);
}
补充: StringBuilder
, StringBufferu
通过 append 进行拼接 不会产生 额外 的对象, 是在原来的字符串上完成拼接。
所以 String
与 StringBuilder
, StringBufferu
的 区别,就是 Stirng
每次 通过 + 拼接, 会产生新的对象 , 这里我们上面 三个 拼接 1w次, 就会有明显 的 区别 。
运行 程序会明显的发现我们的 String 类 拼接的 时间 是 比 StringBuilder, StringBufferu 要慢的多 。
这里我们 就需要注意: 如果在循环 中拼接 字符串,我们 要尽量 不用使用 String
, 尽量使用 StringBuilder
, StringBufferu
下面我们就来 学习 一下 StringBuilder
和 StringBufferu
StringBuilder和StringBuffer
前引 :
刚刚说过, 通过String 拼接是非常慢的 为什么 慢 , 慢到哪里 呢 ?
这里我们就需要 观察一下 他的 汇编 ,
这里 我们 可以 通过 StringBuilder 还原 成 上面 String 拼接的 效果
这个代码 就与 String 拼接的 代码 是 等价的。
下面就来 改进 用一个 StringBuilder 来完成我们的任务 ,这样完成了我们的简化,少了频繁 new 对象的 过程。
下面就来对 讲一讲 StringBuilder
和 StringBuffer
的 区别
StringBuilder 和 StringBuffer的区别
区别 | StringBuilder | StringBuffer |
---|---|---|
相同点 | 是 一个 类 能表达字符串 | 是一个类 能表达字符串 |
相同点 | 不能直接 赋值 | 不能直接赋值 |
不同点 | 线程不安全 | 线程安全的 |
图示: StringBuilder 和 StringBuffer 不能直接 赋值
这里我们就必须 new ,通过 构造方法,完成我们 的 赋值。
如果我们 通过 sout 直接打印 这两个对象是 能 成功的打印出 字符串的,这 是 为什么呢?
这里就与我们的线程安全 有关 , 这里我们 我们的 StringBuffer 是 线程安全的, 而 StringBuilder 是 线程不安全的, 啥是线程安全呢?
这里 暂且 提一嘴 , 后面学到线程的时候 详细讲解 , 这里 只要知道 这两个类那个是线程安全的 那个是线程 不安全的 就好。
这里就来简单举个例子 :
通过上面 队 StringBuilder 和 StringBuffer 的讲解, 下面就来看 个面试 题
String、StringBuffer、StringBuilder的区别
答案 :
1.String的内容不可修改,StringBuffer与StringBuilder的内容可以修改.
2.StringBuffer与StringBuilder大部分功能是相似的
3.StringBuffer采用同步处理,属于线程安全操作;而StringBuilder未采用同步处理,属于线程不安全操作
最后 我们在来 完成几道 力扣 的 编程题 结束我们的 String类学习
String类 oj题
本题就一个难点, 如何 将 不是 字母 字符和 数组字符的排序,这里我们就可以单独写一个方法来判断, 如果 是 在 a 和 z 之间,或 0 到 9 之间就满足我们的条件,返回 true , 不是 返回 false 让 此时的 left 或 right 改变即可, 最后比较。 如果相等left ++ 和 right–,如果 不相等返回 false 就不是 回文串。 这我们同样可以使用 String类自带的方法。 这里就不使用了, 思路是一样 的,可以 自行了解
class Solution {
public static boolean isValidChar(char ch){
if((ch >= 'a' && ch <= 'z')|| (ch >= '0' && ch <= '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 && !isValidChar(s.charAt(left))){
//此时说明不是 字母
left++;
}
while(left < right && !isValidChar(s.charAt(right))){
right--;
}
// 走到这里说明left 和 right 处 都是字母
if(s.charAt(left) != s.charAt(right)){
return false;
}else {
left++;
right--;
}
}
return true;
}
}
使用 String类 自带的方法
class Solution {
public boolean isPalindrome(String s) {
int n = s.length();
int left = 0;
int right = n-1;
while(left <= right) {
while(left < right &&!Character.isLetterOrDigit(s.charAt(left))){
left++;
}
while(left< right &&!Character.isLetterOrDigit(s.charAt(right))) {
right--;
}
if(Character.toLowerCase(s.charAt(left)) != Character.toLowerCase(s.charAt(right))){
return false;
}
left++;
right--;
}
return true;
}
}
本文结束 , 下文 预告: 异常。