大家都知道String+String會開銷額外的系統資源,粗略的原因是String是不可變類,每一步操作都會返回新的String變量,占用空間及時間。
其實我的理解不是這樣的,我們來看看String+的底層實現。
測試案例
public static void main(String[] args) { String a = "a"; StringBuilder b = new StringBuilder("b"); StringBuffer c = new StringBuffer("c"); long star = System.currentTimeMillis(); for(int i=0;i<200000;i++){ a+="a"; } long end = System.currentTimeMillis(); System.out.println("String:"+(end-star)); star = System.currentTimeMillis(); for(int i=0;i<200000;i++){ b.append("b"); } end = System.currentTimeMillis(); System.out.println("StringBuilder:"+(end-star)); star = System.currentTimeMillis(); for(int i=0;i<200000;i++){ c.append("c"); } end = System.currentTimeMillis(); System.out.println("StringBuffer:"+(end-star)); }
測試結果
String:17735
StringBuilder:6
StringBuffer:7
測試結論
String+ 確實占用了太多的資源,處理效率非常低下。StringBuilder比StringBuffer更新了同步方法,性能有所提升。
原因分析
我用javap -verbose 查看已經編譯好的class文件發現:
public static void main(java.lang.String[]); flags: ACC_PUBLIC, ACC_STATIC Code: stack=6, locals=9, args_size=1 0: ldc #16 // String a 2: astore_1 3: new #18 // class java/lang/StringBuilder 6: dup 7: ldc #20 // String b 9: invokespecial #22 // Method java/lang/StringBuilder. <init>":(Ljava/lang/String;)V 12: astore_2 13: new #25 // class java/lang/StringBuffer 16: dup 17: ldc #27 // String c 19: invokespecial #29 // Method java/lang/StringBuffer." init>":(Ljava/lang/String;)V 22: astore_3 23: invokestatic #30 // Method java/lang/System.current imeMillis:()J 26: lstore 4 28: iconst_0 29: istore 6 31: goto 57 34: new #18 // class java/lang/StringBuilder 37: dup 38: aload_1 39: invokestatic #36 // Method java/lang/String.valueOf (Ljava/lang/Object;)Ljava/lang/String; 42: invokespecial #22 // Method java/lang/StringBuilder. <init>":(Ljava/lang/String;)V 45: ldc #16 // String a 47: invokevirtual #42 // Method java/lang/StringBuilder. ppend:(Ljava/lang/String;)Ljava/lang/StringBuilder; 50: invokevirtual #46 // Method java/lang/StringBuilder. oString:()Ljava/lang/String; 53: astore_1 54: iinc 6, 1 57: iload 6 59: ldc #50 // int 100000 61: if_icmplt 34 64: invokestatic #30 // Method java/lang/System.current imeMillis:()J 67: lstore 6 69: getstatic #51 // Field java/lang/System.out:Ljav /io/PrintStream; 72: new #18 // class java/lang/StringBuilder 75: dup 76: ldc #55 // String String: 78: invokespecial #22 // Method java/lang/StringBuilder. <init>":(Ljava/lang/String;)V 81: lload 6 83: lload 4 85: lsub 86: invokevirtual #57 // Method java/lang/StringBuilder. ppend:(J)Ljava/lang/StringBuilder; 89: invokevirtual #46 // Method java/lang/StringBuilder. oString:()Ljava/lang/String; 92: invokevirtual #60 // Method java/io/PrintStream.prin ln:(Ljava/lang/String;)V 95: invokestatic #30 // Method java/lang/System.current imeMillis:()J 98: lstore 4 100: iconst_0 101: istore 8 103: goto 116 106: aload_2 107: ldc #20 // String b 109: invokevirtual #42 // Method java/lang/StringBuilder. ppend:(Ljava/lang/String;)Ljava/lang/StringBuilder; 112: pop 113: iinc 8, 1 116: iload 8 118: ldc #50 // int 100000 120: if_icmplt 106 123: invokestatic #30 // Method java/lang/System.current imeMillis:()J 126: lstore 6 128: getstatic #51 // Field java/lang/System.out:Ljav /io/PrintStream; 131: new #18 // class java/lang/StringBuilder 134: dup 135: ldc #65 // String StringBuilder: 137: invokespecial #22 // Method java/lang/StringBuilder. <init>":(Ljava/lang/String;)V 140: lload 6 142: lload 4 144: lsub 145: invokevirtual #57 // Method java/lang/StringBuilder. ppend:(J)Ljava/lang/StringBuilder; 148: invokevirtual #46 // Method java/lang/StringBuilder. oString:()Ljava/lang/String; 151: invokevirtual #60 // Method java/io/PrintStream.prin ln:(Ljava/lang/String;)V 154: invokestatic #30 // Method java/lang/System.current imeMillis:()J 157: lstore 4 159: iconst_0 160: istore 8 162: goto 175 165: aload_3 166: ldc #27 // String c 168: invokevirtual #67 // Method java/lang/StringBuffer.a pend:(Ljava/lang/String;)Ljava/lang/StringBuffer; 171: pop 172: iinc 8, 1 175: iload 8 177: ldc #50 // int 100000 179: if_icmplt 165 182: invokestatic #30 // Method java/lang/System.current imeMillis:()J 185: lstore 6 187: getstatic #51 // Field java/lang/System.out:Ljav /io/PrintStream; 190: new #18 // class java/lang/StringBuilder 193: dup 194: ldc #70 // String StringBuffer: 196: invokespecial #22 // Method java/lang/StringBuilder. <init>":(Ljava/lang/String;)V 199: lload 6 201: lload 4 203: lsub 204: invokevirtual #57 // Method java/lang/StringBuilder. ppend:(J)Ljava/lang/StringBuilder; 207: invokevirtual #46 // Method java/lang/StringBuilder. oString:()Ljava/lang/String; 210: invokevirtual #60 // Method java/io/PrintStream.prin ln:(Ljava/lang/String;)V 213: return
從編譯的代碼來看,String+的准確操作是:
new StringBuilder()
new String.valueof()
StringBuilder.<init>
StringBuilder.append()
StringBuilder.toString()
而StringBuilder的准確操作是:
StringBuilder.append()
而StringBuffer()的准確操作是:
StringBuffer.append
轉自http://alqm1314-126-com.iteye.com/blog/1932879