为什么需要StringBuilder和StringBuffer
因为 String类与字符串操作 里说了,String是不可变的,每次修改都会创建新对象
打个比方:String就像用石头刻字 ,改一个字就要换一块新石头;StringBuilder就像用白板写字 ,随便擦随便改,始终是同一块板子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Test public void testWhyNeedStringBuilder () {String s = "" ;for (int i = 0 ; i < 5 ; i++) { s = s + (char ) ('a' + i); } StringBuilder sb = new StringBuilder ();for (int i = 0 ; i < 5 ; i++) { sb.append((char ) ('a' + i)); } System.out.println(sb.toString()); }
StringBuilder:单线程首选
线程不安全但快 ,日常开发90%的场景用它就对了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Test public void testStringBuilder () {StringBuilder sb1 = new StringBuilder (); StringBuilder sb2 = new StringBuilder ("hello" ); StringBuilder sb3 = new StringBuilder (100 ); sb1.append("hello" ); sb1.append(" " ); sb1.append("world" ); sb1.append(123 ); System.out.println(sb1); StringBuilder sb = new StringBuilder ();sb.append("姓名:" ).append("张三" ).append(",年龄:" ).append(18 ); System.out.println(sb); }
StringBuffer:多线程用它
线程安全但慢 ,方法上加了 synchronized 关键字
用法和StringBuilder一模一样,只是多线程环境才需要用
1 2 3 4 5 6 7 8 9 10 11 12 @Test public void testStringBuffer () {StringBuffer sbf = new StringBuffer ();sbf.append("多线程" ); sbf.append("安全" ); System.out.println(sbf.toString()); }
常用方法详解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 @Test public void testCommonMethods () {StringBuilder sb = new StringBuilder ("hello world" );sb.append("!" ); System.out.println(sb); sb.insert(5 , "," ); System.out.println(sb); sb.delete(5 , 7 ); System.out.println(sb); sb.replace(5 , 10 , " Java" ); System.out.println(sb); sb.reverse(); System.out.println(sb); String result = sb.toString();System.out.println(result); sb = new StringBuilder ("abc" ); System.out.println(sb.length()); System.out.println(sb.charAt(1 )); }
方法
作用
示例
结果
append(x)
末尾追加
sb.append("!")
“hello!”
insert(i, x)
指定位置插入
sb.insert(0, "hi")
“hihello”
delete(a, b)
删除[a,b)
sb.delete(0, 2)
“llo”
replace(a, b, s)
替换[a,b)
sb.replace(0, 5, "hi")
“hi”
reverse()
反转
sb.reverse()
“olleh”
toString()
转成String
sb.toString()
“hello”
String vs StringBuilder vs StringBuffer 对比
特性
String
StringBuilder
StringBuffer
可变性
不可变
可变
可变
线程安全
安全(不可变天生安全)
不安全
安全 (synchronized)
性能
拼接最慢
最快
比StringBuilder稍慢
使用场景
字符串不怎么变化
单线程拼接 (首选)
多线程拼接(少见)
出现版本
JDK 1.0
JDK 1.5
JDK 1.0
选择口诀
字符串基本不变 → String
单线程频繁拼接 → StringBuilder(99%的情况)
多线程频繁拼接 → StringBuffer(很少遇到)
实战:循环拼接字符串的正确写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 @Test public void testLoopConcat () {int [] ids = {1 , 3 , 5 , 7 , 9 };StringBuilder sql = new StringBuilder ("SELECT * FROM users WHERE id IN (" );for (int i = 0 ; i < ids.length; i++) { if (i > 0 ) { sql.append(", " ); } sql.append(ids[i]); } sql.append(")" ); System.out.println(sql); String[] names = {"张三" , "李四" , "王五" }; StringBuilder csv = new StringBuilder ();for (int i = 0 ; i < names.length; i++) { if (i > 0 ) { csv.append("," ); } csv.append(names[i]); } System.out.println(csv); String joined = String.join("," , names);System.out.println(joined); }
小技巧 :如果你知道最终字符串大概多长,可以在构造时指定容量,避免内部数组扩容
1 2 StringBuilder sb = new StringBuilder (200 );
常见坑
坑
说明
StringBuilder不能用 == 或 equals 比较内容
StringBuilder没有重写equals,比较内容要先 toString() 再比
循环里每次new StringBuilder
应该在循环外面创建,循环里只append
忘了调 toString()
StringBuilder传给需要String的方法时要转换
多线程用StringBuilder
数据可能错乱,多线程请用StringBuffer或加锁
练习题
题1:用StringBuilder实现字符串反转
1 2 3 4 5 6 @Test public void testReverseWithSB () {String input = "Hello World" ;String reversed = new StringBuilder (input).reverse().toString();System.out.println(reversed); }
题2:把数组转成 “[1, 2, 3, 4, 5]” 格式的字符串
1 2 3 4 5 6 7 8 9 10 11 @Test public void testArrayToString () {int [] arr = {1 , 2 , 3 , 4 , 5 };StringBuilder sb = new StringBuilder ("[" );for (int i = 0 ; i < arr.length; i++) { if (i > 0 ) sb.append(", " ); sb.append(arr[i]); } sb.append("]" ); System.out.println(sb); }
题3:统计StringBuilder的扩容过程
1 2 3 4 5 6 7 8 9 @Test public void testCapacity () {StringBuilder sb = new StringBuilder (); System.out.println("初始容量:" + sb.capacity()); sb.append("12345678901234567" ); System.out.println("扩容后容量:" + sb.capacity()); }