日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

String拼接字符串效率低,你知道原因吗?

發布時間:2023/11/27 生活经验 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 String拼接字符串效率低,你知道原因吗? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

面試官Q1:請問為什么String用"+"拼接字符串效率低下,最好能從JVM角度談談嗎?

對于這個問題,我們先來看看如下代碼:

public class StringTest {public static void main(String[] args) {String a = "abc";String b = "def";String c = a + b;String d = "abc" + "def";System.out.Println(c);System.out.Println(d);}
}

打印結果:

abcdef
abcdef

從上面代碼示例中,我們看到兩種方式拼接的字符串打印的結果是一樣的。但這只是表面上的,實際內部運行不一樣。

兩者究竟有什么不一樣?

為了看到兩者的不同,對代碼做如下調整:

public class StringTest {public static void main(String[] args) {String a = "abc";String b = "def";String c = a + b;System.out.Println(c);}
}

我們看看編譯完成后它是什么樣子:

C:\Users\GRACE\Documents>javac StringTest.java2C:\Users\GRACE\Documents>javap -verbose StringTest3Classfile /C:/Users/GRACE/Documents/StringTest.class4  Last modified 2018-7-21; size 607 bytes5  MD5 checksum a2729f11e22d7e1153a209e5ac968b986  Compiled from "StringTest.java"7public class StringTest8  minor version: 09  major version: 52
10  flags: ACC_PUBLIC, ACC_SUPER
11Constant pool:
12   #1 = Methodref          #11.#20        // java/lang/Object."<init>":()V
13   #2 = String             #21            // abc
14   #3 = String             #22            // def
15   #4 = Class              #23            // java/lang/StringBuilder
16   #5 = Methodref          #4.#20         // java/lang/StringBuilder."<init>":()V
17   #6 = Methodref          #4.#24         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
18   #7 = Methodref          #4.#25         // java/lang/StringBuilder.toString:()Ljava/lang/String;
19   #8 = Fieldref           #26.#27        // java/lang/System.out:Ljava/io/PrintStream;
20   #9 = Methodref          #28.#29        // java/io/PrintStream.println:(Ljava/lang/String;)V
21  #10 = Class              #30            // StringTest
22  #11 = Class              #31            // java/lang/Object
23  #12 = Utf8               <init>
24  #13 = Utf8               ()V
25  #14 = Utf8               Code
26  #15 = Utf8               LineNumberTable
27  #16 = Utf8               main
28  #17 = Utf8               ([Ljava/lang/String;)V
29  #18 = Utf8               SourceFile
30  #19 = Utf8               StringTest.java
31  #20 = NameAndType        #12:#13        // "<init>":()V
32  #21 = Utf8               abc
33  #22 = Utf8               def
34  #23 = Utf8               java/lang/StringBuilder
35  #24 = NameAndType        #32:#33        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36  #25 = NameAndType        #34:#35        // toString:()Ljava/lang/String;
37  #26 = Class              #36            // java/lang/System
38  #27 = NameAndType        #37:#38        // out:Ljava/io/PrintStream;
39  #28 = Class              #39            // java/io/PrintStream
40  #29 = NameAndType        #40:#41        // println:(Ljava/lang/String;)V
41  #30 = Utf8               StringTest
42  #31 = Utf8               java/lang/Object
43  #32 = Utf8               append
44  #33 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
45  #34 = Utf8               toString
46  #35 = Utf8               ()Ljava/lang/String;
47  #36 = Utf8               java/lang/System
48  #37 = Utf8               out
49  #38 = Utf8               Ljava/io/PrintStream;
50  #39 = Utf8               java/io/PrintStream
51  #40 = Utf8               println
52  #41 = Utf8               (Ljava/lang/String;)V
53{
54  public StringTest();
55    descriptor: ()V
56    flags: ACC_PUBLIC
57    Code:
58      stack=1, locals=1, args_size=1
59         0: aload_0
60         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
61         4: return
62      LineNumberTable:
63        line 1: 0
64
65  public static void main(java.lang.String[]);
66    descriptor: ([Ljava/lang/String;)V
67    flags: ACC_PUBLIC, ACC_STATIC
68    Code:
69      stack=2, locals=4, args_size=1
70         0: ldc           #2                  // String abc
71         2: astore_1
72         3: ldc           #3                  // String def
73         5: astore_2
74         6: new           #4                  // class java/lang/StringBuilder
75         9: dup
76        10: invokespecial #5                  // Method java/lang/StringBuilder."<init>":()V
77        13: aload_1
78        14: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
79        17: aload_2
80        18: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
81        21: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
82        24: astore_3
83        25: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
84        28: aload_3
85        29: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
86        32: return
87      LineNumberTable:
88        line 3: 0
89        line 4: 3
90        line 5: 6
91        line 6: 25
92        line 7: 32
93}
94SourceFile: "StringTest.java"

首先看到使用了一個指針指向一個常量池中的對象內容為“abc”,而另一個指針指向“def”,此時通過new申請了一個StringBuilder,然后調用這個StringBuilder的初始化方法;然后分別做了兩次append操作,然后最后做一個toString()操作;可見String的+在編譯后會被編譯為StringBuilder來運行,我們知道這里做了一個new StringBuilder的操作,并且做了一個toString的操作,如果你對JVM有所了解,凡是new出來的對象絕對不會放在常量池中,toString會發生一次內容拷貝,但是也不會在常量池中,所以在這里常量池String+常量池String放在了堆中。

?

我們再來看看另外一種情況,用同樣的方式來看看結果是什么:

代碼如下:

public class StringTest {public static void main(String[] args) {String c = "abc" + "def";System.out.println(c);}
}

我們也來看看它編譯完成后是什么樣子:

C:\Users\GRACE\Documents>javac StringTest.java23C:\Users\GRACE\Documents>javap -verbose StringTest4Classfile /C:/Users/GRACE/Documents/StringTest.class5  Last modified 2018-7-21; size 426 bytes6  MD5 checksum c659d48ff8aeb45a3338dea5d129f5937  Compiled from "StringTest.java"8public class StringTest9  minor version: 0
10  major version: 52
11  flags: ACC_PUBLIC, ACC_SUPER
12Constant pool:
13   #1 = Methodref          #6.#15         // java/lang/Object."<init>":()V
14   #2 = String             #16            // abcdef
15   #3 = Fieldref           #17.#18        // java/lang/System.out:Ljava/io/PrintStream;
16   #4 = Methodref          #19.#20        // java/io/PrintStream.println:(Ljava/lang/String;)V
17   #5 = Class              #21            // StringTest
18   #6 = Class              #22            // java/lang/Object
19   #7 = Utf8               <init>
20   #8 = Utf8               ()V
21   #9 = Utf8               Code
22  #10 = Utf8               LineNumberTable
23  #11 = Utf8               main
24  #12 = Utf8               ([Ljava/lang/String;)V
25  #13 = Utf8               SourceFile
26  #14 = Utf8               StringTest.java
27  #15 = NameAndType        #7:#8          // "<init>":()V
28  #16 = Utf8               abcdef
29  #17 = Class              #23            // java/lang/System
30  #18 = NameAndType        #24:#25        // out:Ljava/io/PrintStream;
31  #19 = Class              #26            // java/io/PrintStream
32  #20 = NameAndType        #27:#28        // println:(Ljava/lang/String;)V
33  #21 = Utf8               StringTest
34  #22 = Utf8               java/lang/Object
35  #23 = Utf8               java/lang/System
36  #24 = Utf8               out
37  #25 = Utf8               Ljava/io/PrintStream;
38  #26 = Utf8               java/io/PrintStream
39  #27 = Utf8               println
40  #28 = Utf8               (Ljava/lang/String;)V
41{
42  public StringTest();
43    descriptor: ()V
44    flags: ACC_PUBLIC
45    Code:
46      stack=1, locals=1, args_size=1
47         0: aload_0
48         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
49         4: return
50      LineNumberTable:
51        line 1: 0
52
53  public static void main(java.lang.String[]);
54    descriptor: ([Ljava/lang/String;)V
55    flags: ACC_PUBLIC, ACC_STATIC
56    Code:
57      stack=2, locals=2, args_size=1
58         0: ldc           #2                  // String abcdef
59         2: astore_1
60         3: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
61         6: aload_1
62         7: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
63        10: return
64      LineNumberTable:
65        line 3: 0
66        line 4: 3
67        line 5: 10
68}
69SourceFile: "StringTest.java"

這一次編譯完后的代碼比前面少了很多,而且,仔細看,你會發現14行處,編譯的過程中直接變成了"abcdef",這是為什么呢?因為當發生“abc” + “def”在同一行發生時,JVM在編譯時就認為這個加號是沒有用處的,編譯的時候就直接變成

String d = "abcdef";

同理如果出現:String a =“a” + 1,編譯時候就會變成:String a = “a1″;

?

再補充一個例子:

final String a = "a";
final String b = "ab";
String c = a + b;

在編譯時候,c部分會被編譯為:String c = “aab”;但是如果a或b有任意一個不是final的,都會new一個新的對象出來;其次再補充下,如果a和b,是某個方法返回回來的,不論方法中是final類型的還是常量什么的,都不會被在編譯時將數據編譯到常量池,因為編譯器并不會跟蹤到方法體里面去看你做了什么,其次只要是變量就是可變的,即使你認為你看到的代碼是不可變的,但是運行時是可以被切入的。

那么效率問題從何說起?

那說了這么多,也沒看到有說效率方面的問題呀?

?

其實上面兩個例子,連接字符串行表達式很簡單,那么"+"和StringBuilder基本是一樣的,但如果結構比較復雜,如使用循環來連接字符串,那么產生的Java Byte Code就會有很大的區別。我們再來看看下面一段代碼:

import java.util.*;
public class StringTest {public static void main(String[] args){String s = "";Random rand = new Random();for (int i = 0; i < 10; i++){s = s + rand.nextInt(1000) + " ";}System.out.println(s);}
}

上面代碼反編譯后的結果如下:

C:\Java\jdk1.8.0_171\bin>javap -c E:\StringTest.class
Picked up _JAVA_OPTIONS: -Xmx512M
Compiled from "StringTest.java"
public class StringTest {public StringTest();Code:0: aload_01: invokespecial #8                  // Method java/lang/Object."<init>":()V4: returnpublic static void main(java.lang.String[]);Code://String s = "";0: ldc           #16                 // String2: astore_1//Random rand = new Random();3: new           #18                 // class java/util/Random6: dup7: invokespecial #20                 // Method java/util/Random."<init>":()V10: astore_2//StringBuilder result = new StringBuilder();11: iconst_012: istore_313: goto          49//s = (new StringBuilder(String.valueOf(s))).append(rand.nextInt(1000)).append(" ").toString();16: new           #21                 // class java/lang/StringBuilder19: dup20: aload_121: invokestatic  #23                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;24: invokespecial #29                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V27: aload_228: sipush        100031: invokevirtual #32                 // Method java/util/Random.nextInt:(I)I34: invokevirtual #36                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;37: ldc           #40                 // String39: invokevirtual #42                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;42: invokevirtual #45                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;45: astore_146: iinc          3, 149: iload_350: bipush        1052: if_icmplt     16//System.out.println(s);55: getstatic     #49                 // Field java/lang/System.out:Ljava/io/PrintStream;58: aload_159: invokevirtual #55                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V62: return
}

我們可以看到,雖然編譯器將"+"轉換成了StringBuilder,但創建StringBuilder對象的位置卻在for語句內部。這就意味著每執行一次循環,就會創建一個StringBuilder對象(對于本例來說,是創建了10StringBuilder對象),雖然Java有垃圾回收器,但這個回收器的工作時間是不定的。如果不斷產生這樣的垃圾,那么仍然會占用大量的資源。解決這個問題的方法就是在程序中直接使用StringBuilder來連接字符串,代碼如下:

import java.util.Random;
public class StringTest {public static void main(String[] args) {Random rand = new Random();StringBuilder result = new StringBuilder();for (int i = 0; i < 10; i++) {result.append(rand.nextInt(1000));result.append(" ");}System.out.println(result.toString());}
}

上面代碼反編譯后的結果如下:

C:\Java\jdk1.8.0_171\bin>javap -c E:\Dubbo\Demo\bin\StringTest.class
Picked up _JAVA_OPTIONS: -Xmx512M
Compiled from "StringTest.java"
public class StringTest {public StringTest();Code:0: aload_01: invokespecial #8                  // Method java/lang/Object."<init>":()V4: returnpublic static void main(java.lang.String[]);Code://Random rand = new Random();0: new           #16                 // class java/util/Random3: dup4: invokespecial #18                 // Method java/util/Random."<init>":()V7: astore_1//StringBuilder result = new StringBuilder();8: new           #19                 // class java/lang/StringBuilder11: dup12: invokespecial #21                 // Method java/lang/StringBuilder."<init>":()V15: astore_2//for(int i = 0; i < 10; i++)16: iconst_017: istore_318: goto          43//result.append(rand.nextInt(1000));21: aload_222: aload_123: sipush        100026: invokevirtual #22                 // Method java/util/Random.nextInt:(I)I29: invokevirtual #26                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;32: pop//result.append(" ");33: aload_234: ldc           #30                 // String36: invokevirtual #32                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;39: pop40: iinc          3, 143: iload_344: bipush        1046: if_icmplt     21//System.out.println(result.toString());49: getstatic     #35                 // Field java/lang/System.out:Ljava/io/PrintStream;52: aload_253: invokevirtual #41                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;56: invokevirtual #45                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V59: return
}

從上面的反編譯結果可以看出,創建StringBuilder的代碼被放在了for語句外。雖然這樣處理在源程序中看起來復雜,但卻換來了更高的效率,同時消耗的資源也更少了。

?

所以,從上述幾個例子中我們得出的結論是:String采用連接運算符(+)效率低下,都是上述循環、大批量數據情況造成的,每做一次"+"就產生個StringBuilder對象,然后append后就扔掉。下次循環再到達時重新產生個StringBuilder對象,然后append字符串,如此循環直至結束。如果我們直接采用StringBuilder對象進行append的話,我們可以節省創建和銷毀對象的時間。如果只是簡單的字面量拼接或者很少的字符串拼接,性能都是差不多的。

C:\Users\GRACE\Documents>javac?StringTest.java
2C:\Users\GRACE\Documents>javap?-verbose?StringTest
3Classfile?/C:/Users/GRACE/Documents/StringTest.class
4??Last?modified?2018-7-21;?size?607?bytes
5??MD5?checksum?a2729f11e22d7e1153a209e5ac968b98
6??Compiled?from?"StringTest.java"
7public?class?StringTest
8??minor?version:?0
9??major?version:?52
10??flags:?ACC_PUBLIC,?ACC_SUPER
11Constant?pool:
12???#1?=?Methodref??????????#11.#20????????//?java/lang/Object."<init>":()V
13???#2?=?String?????????????#21????????????//?abc
14???#3?=?String?????????????#22????????????//?def
15???#4?=?Class??????????????#23????????????//?java/lang/StringBuilder
16???#5?=?Methodref??????????#4.#20?????????//?java/lang/StringBuilder."<init>":()V
17???#6?=?Methodref??????????#4.#24?????????//?java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
18???#7?=?Methodref??????????#4.#25?????????//?java/lang/StringBuilder.toString:()Ljava/lang/String;
19???#8?=?Fieldref???????????#26.#27????????//?java/lang/System.out:Ljava/io/PrintStream;
20???#9?=?Methodref??????????#28.#29????????//?java/io/PrintStream.println:(Ljava/lang/String;)V
21??#10?=?Class??????????????#30????????????//?StringTest
22??#11?=?Class??????????????#31????????????//?java/lang/Object
23??#12?=?Utf8???????????????<init>
24??#13?=?Utf8???????????????()V
25??#14?=?Utf8???????????????Code
26??#15?=?Utf8???????????????LineNumberTable
27??#16?=?Utf8???????????????main
28??#17?=?Utf8???????????????([Ljava/lang/String;)V
29??#18?=?Utf8???????????????SourceFile
30??#19?=?Utf8???????????????StringTest.java
31??#20?=?NameAndType????????#12:#13????????//?"<init>":()V
32??#21?=?Utf8???????????????abc
33??#22?=?Utf8???????????????def
34??#23?=?Utf8???????????????java/lang/StringBuilder
35??#24?=?NameAndType????????#32:#33????????//?append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36??#25?=?NameAndType????????#34:#35????????//?toString:()Ljava/lang/String;
37??#26?=?Class??????????????#36????????????//?java/lang/System
38??#27?=?NameAndType????????#37:#38????????//?out:Ljava/io/PrintStream;
39??#28?=?Class??????????????#39????????????//?java/io/PrintStream
40??#29?=?NameAndType????????#40:#41????????//?println:(Ljava/lang/String;)V
41??#30?=?Utf8???????????????StringTest
42??#31?=?Utf8???????????????java/lang/Object
43??#32?=?Utf8???????????????append
44??#33?=?Utf8???????????????(Ljava/lang/String;)Ljava/lang/StringBuilder;
45??#34?=?Utf8???????????????toString
46??#35?=?Utf8???????????????()Ljava/lang/String;
47??#36?=?Utf8???????????????java/lang/System
48??#37?=?Utf8???????????????out
49??#38?=?Utf8???????????????Ljava/io/PrintStream;
50??#39?=?Utf8???????????????java/io/PrintStream
51??#40?=?Utf8???????????????println
52??#41?=?Utf8???????????????(Ljava/lang/String;)V
53{
54??public?StringTest();
55????descriptor:?()V
56????flags:?ACC_PUBLIC
57????Code:
58??????stack=1,?locals=1,?args_size=1
59?????????0:?aload_0
60?????????1:?invokespecial?#1??????????????????//?Method?java/lang/Object."<init>":()V
61?????????4:?return
62??????LineNumberTable:
63????????line?1:?0
64
65??public?static?void?main(java.lang.String[]);
66????descriptor:?([Ljava/lang/String;)V
67????flags:?ACC_PUBLIC,?ACC_STATIC
68????Code:
69??????stack=2,?locals=4,?args_size=1
70?????????0:?ldc???????????#2??????????????????//?String?abc
71?????????2:?astore_1
72?????????3:?ldc???????????#3??????????????????//?String?def
73?????????5:?astore_2
74?????????6:?new???????????#4??????????????????//?class?java/lang/StringBuilder
75?????????9:?dup
76????????10:?invokespecial?#5??????????????????//?Method?java/lang/StringBuilder."<init>":()V
77????????13:?aload_1
78????????14:?invokevirtual?#6??????????????????//?Method?java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
79????????17:?aload_2
80????????18:?invokevirtual?#6??????????????????//?Method?java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
81????????21:?invokevirtual?#7??????????????????//?Method?java/lang/StringBuilder.toString:()Ljava/lang/String;
82????????24:?astore_3
83????????25:?getstatic?????#8??????????????????//?Field?java/lang/System.out:Ljava/io/PrintStream;
84????????28:?aload_3
85????????29:?invokevirtual?#9??????????????????//?Method?java/io/PrintStream.println:(Ljava/lang/String;)V
86????????32:?return
87??????LineNumberTable:
88????????line?3:?0
89????????line?4:?3
90????????line?5:?6
91????????line?6:?25
92????????line?7:?32
93}
94SourceFile:?"StringTest.java"

轉載于:https://www.cnblogs.com/shanheyongmu/p/9583381.html

總結

以上是生活随笔為你收集整理的String拼接字符串效率低,你知道原因吗?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。