给你一个字符串 s ,将该字符串中的大写字母转换成相同的小写字母,返回新的字符串。
示例 1:
输入:s = “Hello”
输出:“hello”
示例 2:
输入:s = “here”
输出:“here”
示例 3:
输入:s = “LOVELY”
输出:“lovely”
提示:1 <= s.length <= 100s
由 ASCII 字符集中的可打印字符组成
ASCII 码中大小写字母的规律
在 ASCII 编码中:
- 大写字母 A-Z 的范围是
65 ('A')
到90 ('Z')
。 - 小写字母 a-z 的范围是
97 ('a')
到122 ('z')
。
观察它们的二进制表示:
'A'
(65)→01000001
'a'
(97)→01100001
- 区别仅在于 第 5 位(从 0 开始算的第 5 位,即
0x20
):- 大写字母的第 5 位是
0
。 - 小写字母的第 5 位是
1
。
- 大写字母的第 5 位是
因此,我们可以通过 位运算 来快速转换大小写。
1. 大写变小写、小写变大写:^=32
(异或 32)
32
的二进制是 00100000
,即 0x20
。
异或(XOR)运算 的特点是:
- 如果某位是
0
,异或1
会翻转它(0 ^ 1 = 1
)。 - 如果某位是
1
,异或1
也会翻转它(1 ^ 1 = 0
)。
所以:
- 大写字母(第 5 位是
0
):'A' ^ 32
→01000001 ^ 00100000 = 01100001
→'a'
(相当于+32
)
- 小写字母(第 5 位是
1
):'a' ^ 32
→01100001 ^ 00100000 = 01000001
→'A'
(相当于-32
)
示例:
char c = 'A';
c ^= 32; // 'A' → 'a'
printf("%c\n", c); // 输出 'a'
c = 'z';
c ^= 32; // 'z' → 'Z'
printf("%c\n", c); // 输出 'Z'
2. 大写变小写、小写保持不变:|=32
(或 32)
|=32
(即 | 0x20
)会强制把第 5 位设为 1
:
- 大写字母(第 5 位是
0
):'A' | 32
→01000001 | 00100000 = 01100001
→'a'
(相当于+32
)
- 小写字母(第 5 位已经是
1
):'a' | 32
→01100001 | 00100000 = 01100001
→'a'
(不变)
示例:
char c = 'B';
c |= 32; // 'B' → 'b'
printf("%c\n", c); // 输出 'b'
c = 'm';
c |= 32; // 'm' → 'm'(不变)
printf("%c\n", c); // 输出 'm'
3. 大写保持不变、小写变大写:&= ~33
(即 &= 0xDF
)
~33
的二进制是 11011111
(因为 33
是 00100001
,取反后是 11011111
)。
&= ~33
会强制把第 5 位设为 0
:
- 大写字母(第 5 位已经是
0
):'A' & ~33
→01000001 & 11011111 = 01000001
→'A'
(不变)
- 小写字母(第 5 位是
1
):'a' & ~33
→01100001 & 11011111 = 01000001
→'A'
(相当于-32
)
示例:
char c = 'C';
c &= ~33; // 'C' → 'C'(不变)
printf("%c\n", c); // 输出 'C'
c = 'n';
c &= ~33; // 'n' → 'N'
printf("%c\n", c); // 输出 'N'
代码示例:
class Solution {
public String toLowerCase(String s) {
StringBuilder sb=new StringBuilder();
for(int i=0;i<s.length();i++){
char ch=s.charAt(i);
if(ch>=65&&ch<=90){
ch|=32;
}
sb.append(ch);
}
return sb.toString();
}
}
说明:
1. append()
的作用
- 功能:将数据(字符串、数字、字符等)追加到可变字符串的末尾。
- 优势:比直接用
+
拼接字符串更高效(尤其适合循环或大量操作),因为不会反复创建新对象。
2. 主要使用场景
(1) StringBuilder
(非线程安全,性能更高)
StringBuilder sb = new StringBuilder();sb.append("Hello"); // 追加字符串sb.append(" ");
// 追加空格sb.append(123);
// 追加数字
System.out.println(sb.toString());
// 输出: "Hello 123"
(2) StringBuffer
(线程安全,性能稍低)
StringBuffer sb = new StringBuffer();
sb.append("线程安全");
sb.append("的拼接");
System.out.println(sb.toString());
// 输出: "线程安全的拼接"
3. 为什么不用 String
直接拼接?
String
不可变:每次+
拼接都会生成新对象,浪费内存。-
StringBuilder/Buffer
可变:
- 直接在原对象上修改,适合频繁操作。
4. 常见用法示例
(1) 链式调用
String result = new StringBuilder().
append("姓名: ").append(name).
append(", 年龄: ").append(age)
.toString();
(2) 循环中高效拼接
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100; i++) {
sb.append(i).append(" ");
}
System.out.println(sb.toString());
5. 注意事项
StringBuilder
vsStringBuffer
:
单线程用StringBuilder
(更快)。
多线程用StringBuffer
(线程安全)。- 初始化容量:若已知最终大小,可指定初始容量避免扩容开销:
StringBuilder sb = new StringBuilder(100); // 初始容量100