java字符串连接 谁践踏了优雅 2022-05-14 12:43 232阅读 0赞 由于String是不可变对象(final),所以,对字符串进行连接、替换操作时,String对象总是会生成新的对象。所以连接和替换时性能很差。 ## String常量字符串的累加 ## 比如我们使用如下代码进行字符串连接: String str = "hello"+"world"+"!"; 先有hello和world2个字符串生成helloworld,然后再生成helloworld!。 将上面的代码做5万次循环。 但上面的代码执行效率竟然比使用StringBuilder快,为什么呢? StringBuilder sb = new StringBuilder(); sb.append("hello"); sb.append("world"); sb.append("!"); 对第一段代码进行反编译,可以看到对于常量字符串的累加,java在编译时就做了优化。 String str = "helloworld!"; ## String变量字符串的累加 ## public class Test { public static void main(String[] args) { String a = "hello"; String b = "world"; String c = "!"; int loopCount = 50000000; long s = System.currentTimeMillis(); for (int i=0;i<loopCount;i++) { // test(a,b,c); test2(a,b,c); } System.out.println("cost " + (System.currentTimeMillis() - s) + "ms."); } private static void test(String a,String b,String c) { StringBuilder stringBuilder = new StringBuilder(); String s = stringBuilder.append(a).append(b).append(c).toString(); } private static void test2(String a,String b,String c) { String s = a + b + c; } } 同样做5万次循环,发现与使用StringBuilder性能差不多。 反编译,发现java在编译时做了优化。 public class Test { public Test() { } public static void main(String[] args) { String a = "hello"; String b = "world"; String c = "!"; int loopCount = 50000000; long s = System.currentTimeMillis(); for(int i = 0; i < loopCount; ++i) { test2(a, b, c); } System.out.println("cost " + (System.currentTimeMillis() - s) + "ms."); } private static void test(String a, String b, String c) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append(a).append(b).append(c).toString(); } private static void test2(String a, String b, String c) { (new StringBuilder()).append(a).append(b).append(c).toString(); } } 可以看到,java在编译的时候将字符串的+操作转换成了使用StringBuilder的append方式。 ## 构建超大的字符串 ## 对下面的代码ABC分别执行10000次。 代码A: String s = ""; for (int i = 0; i < 1000; i++) { s = s + i; } 执行耗时:4542ms。 代码B: String s = ""; for (int i = 0; i < 1000; i++) { s = s.concat(String.valueOf(i)); } 执行耗时:4079ms。 代码C: StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < 1000; i++) { stringBuilder.append(i); } String s = stringBuilder.toString(); 执行耗时:259ms。 可以看到,从快到慢依次是StringBuilder > String.concat() > String+。并且StringBuilder要快很多。 观察编译后的代码发现代码Ajava编译器并没有做任何优化。 ## 选择StringBuilder还是StringBuffer? ## StringBuffer与StringBuilder最大的不同在于,StringBuffer对几乎所有的方法都做了同步,StringBuilder没有做任何同步。 由于方法同步需要消耗一定的系统资源,因此,StringBuffer效率要低于StringBuilder。但是,在多线程环境中,StringBuilder无法保证线程安全,不能使用。 所以,如果是不需要考虑线程安全的情况下,使用StringBuilder;相反则使用StringBuffer。 ## 容量参数 ## 无论是StringBuffer还是StringBuilder都可以在初始化时设置一个容量参数。 public StringBuffer(int capacity); public StringBuilder(int capacity); 在不指定容量参数时,默认是16个字符。 代码如下: public StringBuilder() { super(16); } StringBuffer和StringBuilder都继承自AbstractStringBuilder,AbstractStringBuilder的构造器代码: AbstractStringBuilder(int capacity) { value = new char[capacity]; } ### 不带容量参数测试 ### //StringBuilder sb = new StringBuilder(); StringBuffer sb = new StringBuffer(); for (int i=0;i<10000000;i++) { sb.append(i); } StringBuilder耗时405ms,StringBuffer耗时557ms。 ### 带容量参数测试 ### //StringBuilder sb = new StringBuilder(68888890); StringBuffer sb = new StringBuffer(68888890); for (int i=0;i<10000000;i++) { sb.append(i); } //System.out.println(sb.length()); StringBuilder耗时330ms,StringBuffer耗时464ms。 通过对比,可以看到增加容量参数可以增加StringBuffer和StringBuilder的性能。 > StringBuffer和StringBuilder在执行append方法时,实际是调用父类AbstractStringBuilder的append方法。 > 父类append方法定义如下: public AbstractStringBuilder append(String str) { if (str == null) str = "null"; int len = str.length(); ensureCapacityInternal(count + len); str.getChars(0, len, value, count); count += len; return this; } > 可以看到一个ensureCapacityInternal方法,定义如下: /** * This method has the same contract as ensureCapacity, but is * never synchronized. */ private void ensureCapacityInternal(int minimumCapacity) { // overflow-conscious code if (minimumCapacity - value.length > 0) expandCapacity(minimumCapacity); } /** * This implements the expansion semantics of ensureCapacity with no * size check or synchronization. */ void expandCapacity(int minimumCapacity) { int newCapacity = value.length * 2 + 2; if (newCapacity - minimumCapacity < 0) newCapacity = minimumCapacity; if (newCapacity < 0) { if (minimumCapacity < 0) // overflow throw new OutOfMemoryError(); newCapacity = Integer.MAX_VALUE; } value = Arrays.copyOf(value, newCapacity); } > minimumCapacity就是现在存储的数据的长度+本次append字符串的长度。如果该长度比定义的保存数据的char\[\]的长度大,说明char\[\]存储空间不够,需要进行扩容了。 > > 扩容的逻辑:新的容量为目前数据的容量的2倍+2;如果扩容后的长度仍然小于目前数据的长度+本次append字符串的长度,则新的容量为目前数据的长度+本次append字符串的长度。 > 然后执行了一次数组的复制,将旧的数据复制到新的数组中。 所以,如果指定合适的容量,可以避免SringBuilder和StringBuffer的内存复制,这样可以提升append的性能。 这个跟HashMap比较像,HashMap在put数据时,也会在容量不够时进行扩容。 参考:《Java程序性能优化——让你的java程序更快、更稳定》
相关 java中字符串连接 字符串连接符: “+”运算符两侧的操作数中只要有一个是字符串(String)类型,系统会自动将另一个操作数转换为字符串然后连接 示例:连接符“+” int a=1 谁践踏了优雅/ 2022年10月23日 09:51/ 0 赞/ 154 阅读
相关 Java数据库连接字符串 1 、Oracle8 / 8i / 9i数据库(thin模式) Class.forName( " oracle.jdbc.driver ゝ一纸荒年。/ 2022年08月27日 11:55/ 0 赞/ 138 阅读
相关 java 字符串连接 使用“+”连接符让两个字符串进行连接 import java.util.Scanner; import java.util.Arrays; publi ゞ 浴缸里的玫瑰/ 2022年06月17日 22:54/ 0 赞/ 231 阅读
相关 java字符串连接问题 前景 在java核心技术 卷1上有这样一段话构建字符串的时候,每次使用+号都会产生一个新的String对象,会浪费资源,最好用StringBuilder。因为那本书的版本 约定不等于承诺〃/ 2022年05月28日 12:06/ 0 赞/ 163 阅读
相关 java字符串连接 由于String是不可变对象(final),所以,对字符串进行连接、替换操作时,String对象总是会生成新的对象。所以连接和替换时性能很差。 String常量字符串的累加 谁践踏了优雅/ 2022年05月14日 12:43/ 0 赞/ 233 阅读
相关 Java连接字符串 对于已经定义的字符串,可以对其进行各种操作。连接多个字符串是字符串操作中最简单的一种。通过字符串连接,可以将两个或多个字符串、字符、整数和浮点数等类型的数据连成一个更大的字符串 以你之姓@/ 2021年12月14日 18:51/ 0 赞/ 312 阅读
相关 Java中连接字符串 Java中连接字符串 方法:通过“+”操作符和StringBuffer.append()方法来连接字符串,并比较其性能 public class StringCo 太过爱你忘了你带给我的痛/ 2021年09月28日 13:32/ 0 赞/ 418 阅读
相关 Java 实例 - 连接字符串 Java 实例 - 连接字符串以下实例演示了通过 "+" 操作符和StringBuffer.append() 方法来连接字符串,并比较其性能: 灰太狼/ 2020年05月22日 14:06/ 0 赞/ 956 阅读
还没有评论,来说两句吧...