面试题答案
一键面试性能差异分析
- String类:
- String类是不可变的,每次对String进行修改操作(如拼接、替换等),都会创建一个新的String对象。例如:
这里先创建了"Hello"对象,执行拼接操作时又创建了"Hello World"对象,原来的"Hello"对象如果没有其他引用则会等待垃圾回收。频繁的修改操作会导致大量临时对象的创建和销毁,性能开销较大。String str = "Hello"; str = str + " World";
- StringBuilder类:
- StringBuilder是可变的字符序列,主要用于单线程环境下的字符串操作。它的方法不是线程安全的,但性能较高。例如进行字符串拼接时:
直接在原有的字符序列上进行操作,不会创建新的对象(除非内部数组容量不足需要扩容),避免了频繁创建和销毁对象的开销,所以在单线程环境下字符串操作性能优于String。StringBuilder sb = new StringBuilder("Hello"); sb.append(" World");
- StringBuffer类:
- StringBuffer同样是可变的字符序列,但它的方法是线程安全的,通过
synchronized
关键字修饰方法来保证线程安全。例如:
由于线程安全机制的存在,在单线程环境下会有额外的同步开销,其性能比StringBuilder略低。StringBuffer sb = new StringBuffer("Hello"); sb.append(" World");
- StringBuffer同样是可变的字符序列,但它的方法是线程安全的,通过
StringBuilder性能优化
- 初始化容量估计:
- 在创建StringBuilder对象时,如果能预先估计字符串的大致长度,可以指定初始容量。例如,如果预计最终字符串长度为1000个字符:
这样可以减少扩容的次数。因为当StringBuilder内部数组容量不足时,会进行扩容操作,通常是扩容为原来容量的2倍加2,扩容操作涉及到数组的复制等开销。StringBuilder sb = new StringBuilder(1000);
- 减少不必要的方法调用:
- 尽量减少在循环中调用StringBuilder的
toString()
方法。例如:
StringBuilder sb = new StringBuilder(); for (int i = 0; i < 1000; i++) { sb.append(i); // 不建议在此处调用toString(),因为每次调用都会创建一个新的String对象 // String temp = sb.toString(); } String result = sb.toString(); // 只在最终需要String对象时调用
- 尽量减少在循环中调用StringBuilder的
- 批量操作:
- 如果有多个字符或字符串需要添加到StringBuilder中,可以使用
append
方法的重载形式进行批量添加。例如,添加一个字符数组:
相比于逐个字符添加,批量操作可以减少方法调用次数,提高性能。char[] charArray = {'a', 'b', 'c'}; StringBuilder sb = new StringBuilder(); sb.append(charArray);
- 如果有多个字符或字符串需要添加到StringBuilder中,可以使用