StringBuilder बनाम String संघन में जावा में


925

toString()नीचे दिए गए 2 कार्यान्वयनों को देखते हुए , जिसे एक पसंद किया जाता है:

public String toString(){
    return "{a:"+ a + ", b:" + b + ", c: " + c +"}";
}

या

public String toString(){
    StringBuilder sb = new StringBuilder(100);
    return sb.append("{a:").append(a)
          .append(", b:").append(b)
          .append(", c:").append(c)
          .append("}")
          .toString();
}

?

इससे भी महत्वपूर्ण बात यह है कि हमारे पास केवल 3 गुण हैं, इससे कोई फर्क नहीं पड़ सकता है, लेकिन आप किस बिंदु पर +कॉनकैट से स्विच करेंगे StringBuilder?


40
किस बिंदु पर आप StringBuilder पर स्विच करते हैं? जब यह स्मृति या प्रदर्शन को प्रभावित करता है। या जब हो सकता है। यदि आप वास्तव में केवल एक बार एक जोड़े के लिए ऐसा कर रहे हैं, तो कोई चिंता नहीं है। लेकिन अगर आप इसे बार-बार करने जा रहे हैं, तो आपको StringBuilder का उपयोग करते समय एक औसत दर्जे का अंतर देखना चाहिए।
ewall

पैरामीटर में 100 का मतलब क्या है?
आसिफ मुश्ताक

2
@ यूएनकाउनटाउन 100 स्ट्रींगबर्ल का प्रारंभिक आकार है
गैर अनुक्रमिक

@nonsequitor तो अधिकतम वर्ण 100 होंगे?
आसिफ मुश्ताक

10
@ केवल ज्ञात प्रारंभिक आकार नहीं, यदि आप उस स्ट्रिंग का अनुमानित आकार जानते हैं, जिसके साथ आप व्यवहार कर रहे हैं, तो आप बता सकते हैं StringBuilderकि कितना आकार पूर्ववर्ती को आवंटित करना है अन्यथा, यदि यह अंतरिक्ष से बाहर निकलता है, तो आकार बनाकर दोगुना करना होगा नया char[]एरे तब डेटा को कॉपी करता है - जो महंगा है। आप आकार देकर धोखा दे सकते हैं और फिर इस सरणी निर्माण की कोई आवश्यकता नहीं है - इसलिए यदि आपको लगता है कि आपकी स्ट्रिंग ~ 100 चार्ट लंबी होगी तो आप स्ट्रिंगबर्ल को उस आकार में सेट कर सकते हैं और इसे आंतरिक रूप से विस्तारित करना कभी नहीं होगा।
नॉन सीक्वेंटर

जवाबों:


964

संस्करण 1 बेहतर है क्योंकि यह छोटा है और संकलक वास्तव में इसे संस्करण 2 में बदल देगा - कोई भी प्रदर्शन अंतर नहीं।

इससे भी महत्वपूर्ण बात यह है कि हमारे पास केवल 3 गुण हैं इससे कोई फर्क नहीं पड़ सकता है, लेकिन किस बिंदु पर आप कॉनकैट से बिल्डर में स्विच कर सकते हैं?

उस बिंदु पर जहां आप एक लूप में समाप्‍त कर रहे हैं - यह आमतौर पर तब होता है जब कंपाइलर StringBuilderअपने द्वारा स्थानापन्न नहीं कर सकता है।


19
यह सच है लेकिन भाषा का संदर्भ यह भी बताता है कि यह वैकल्पिक है। वास्तव में, मैंने अभी JRE 1.6.0_15 के साथ एक सरल परीक्षण किया था और मुझे विघटित वर्ग में कोई संकलक अनुकूलन नहीं देखा था।
ब्रूनो कोंडे

37
मैंने सिर्फ प्रश्न से कोड की कोशिश की (JDK 1.6.0_16 पर संकलित) और उम्मीद के मुताबिक अनुकूलन पाया। मुझे पूरा यकीन है कि सभी आधुनिक कंपाइलर ऐसा करेंगे।
माइकल बोर्गवर्ड

22
तुम सही हो। बाइटकोड को देखते हुए मैं स्पष्ट रूप से स्ट्रिंगबीयल ऑप्टिमाइज़ेशन देख सकता हूं। मैं एक decompiler का उपयोग कर रहा था और, कुछ कैसे, यह वापस समतल करने के लिए परिवर्तित कर रहा है। +1
ब्रूनो कोंडे

80
नहीं एक मरे हुए घोड़े को हरा, लेकिन कल्पना में शब्दों है: To increase the performance of repeated string concatenation, a Java compiler _may_ use the StringBuffer class or a similar technique to reduce the number of intermediate String objects that are created by evaluation of an expression.कुंजी शब्द वहाँ जा रहा है हो सकता है । यह देखते हुए कि यह आधिकारिक रूप से वैकल्पिक है (हालांकि सबसे अधिक संभावना है) हमें अपनी सुरक्षा नहीं करनी चाहिए?
लुकास

93
@ लुकास: नहीं, हमें नहीं करना चाहिए। यदि कंपाइलर उस ऑप्टिमाइज़ेशन को नहीं करने का फैसला करता है, तो यह इसलिए होगा क्योंकि यह इसके लायक नहीं है। 99% मामलों में, कंपाइलर बेहतर जानता है कि कौन सा अनुकूलन इसके लायक है, इसलिए अंगूठे के नियम के रूप में देव को हस्तक्षेप नहीं करना चाहिए। बेशक, आपकी स्थिति अन्य 1% में गिर सकती है , लेकिन इसे केवल (सावधान) बेंचमार्किंग द्वारा जांचा जा सकता है।
sleske

255

कुंजी यह है कि क्या आप एक ही स्थान पर एक ही संकेतन लिख रहे हैं या इसे समय के साथ जमा कर रहे हैं।

आपके द्वारा दिए गए उदाहरण के लिए, StringBuilder का स्पष्ट रूप से उपयोग करने का कोई मतलब नहीं है। (अपने पहले मामले के लिए संकलित कोड को देखें।)

लेकिन अगर आप एक लूप के अंदर एक स्ट्रिंग बना रहे हैं, तो StringBuilder का उपयोग करें।

स्पष्ट करने के लिए, यह मानते हुए कि विशाल ऐरे में हजारों तार हैं, कोड इस प्रकार है:

...
String result = "";
for (String s : hugeArray) {
    result = result + s;
}

के साथ तुलना में बहुत समय और स्मृति-व्यर्थ है:

...
StringBuilder sb = new StringBuilder();
for (String s : hugeArray) {
    sb.append(s);
}
String result = sb.toString();

3
हां, StringBuilder को स्ट्रिंग ऑब्जेक्ट को बार-बार बनाने की आवश्यकता नहीं है।
ओल्गा

124
दमिति मैं उन 2 फंक्शन का इस्तेमाल करता हूं जो मैं एक बड़ी स्ट्रिंग का परीक्षण करने के लिए काम करता हूं। 6.51min बनाम 11secs
user1722791

1
वैसे आप result += s;(पहले उदाहरण में) का उपयोग कर सकते हैं
RAnders00

1
इस कथन से कितनी वस्तु बनेगी? "{a:"+ a + ", b:" + b + ", c: " + c +"}";
आसिफ मुश्ताक

1
क्या कुछ के बारे में जैसे: स्ट्रिंग str = (a == null)? null: a '+ (b == null)? null: b '+ (c == null)? c: c '+ ...; ? क्या ऐसा होने से अनुकूलन को रोका जा सकेगा?
२२:०६ पर amitfr

75

मैं पसंद करता हूं:

String.format( "{a: %s, b: %s, c: %s}", a, b, c );

... क्योंकि यह छोटा और पठनीय है।

मैं इसे गति के लिए अनुकूलित नहीं करूंगा जब तक कि आप इसे लूप के अंदर एक बहुत उच्च दोहराने वाली गिनती के साथ उपयोग न करें और प्रदर्शन अंतर को मापें।

मैं सहमत हूं, कि यदि आपको बहुत सारे मापदंडों का उत्पादन करना है, तो यह फ़ॉर्म भ्रमित हो सकता है (जैसे टिप्पणियों में से एक कहता है)। इस मामले में मैं एक और अधिक पठनीय रूप पर स्विच करूँगा (शायद मैट-बी के जवाब से लिया गया - अपाचे-कॉमन्स के ToStringBuilder का उपयोग करके ) और फिर से प्रदर्शन को अनदेखा करूं।


64
यह वास्तव में लंबा है, इसमें अधिक प्रतीक हैं और इसमें एक पाठ अनुक्रम से बाहर है।
टॉम हॉन्टिन -

4
तो क्या आप कहेंगे कि यह एक दूसरे की तुलना में कम पठनीय है?
tangens

3
मैं इसे लिखना पसंद करता हूं, क्योंकि अधिक चर जोड़ना आसान है, लेकिन मुझे यकीन नहीं है कि यह अधिक पठनीय है - विशेष रूप से तर्कों की संख्या बड़ी हो जाती है। यह भी समय के लिए काम नहीं करता है जब आपको अलग-अलग समय पर बिट्स जोड़ने की आवश्यकता होती है।
एलेक्स फीमैन

78
पढ़ने में कठिन लगता है (मेरे लिए)। अब मुझे {...} और मापदंडों के बीच आगे और पीछे स्कैन करना होगा।
स्टीव कूओ

10
मैं, तो इस फ़ॉर्म पसंद करते हैं क्योंकि यह सुरक्षित है अगर पैरामीटर में से एक हैnull
आरडीएस

74

ज्यादातर मामलों में, आपको दो दृष्टिकोणों के बीच एक वास्तविक अंतर दिखाई नहीं देगा, लेकिन इस तरह के सबसे खराब स्थिति का निर्माण करना आसान है:

public class Main
{
    public static void main(String[] args)
    {
        long now = System.currentTimeMillis();
        slow();
        System.out.println("slow elapsed " + (System.currentTimeMillis() - now) + " ms");

        now = System.currentTimeMillis();
        fast();
        System.out.println("fast elapsed " + (System.currentTimeMillis() - now) + " ms");
    }

    private static void fast()
    {
        StringBuilder s = new StringBuilder();
        for(int i=0;i<100000;i++)
            s.append("*");      
    }

    private static void slow()
    {
        String s = "";
        for(int i=0;i<100000;i++)
            s+="*";
    }
}

आउटपुट है:

slow elapsed 11741 ms
fast elapsed 7 ms

समस्या यह है कि स्ट्रिंग के लिए + = जोड़ें एक नई स्ट्रिंग को फिर से संगठित करता है, इसलिए यह आपके तार की लंबाई (दोनों का योग) के लिए कुछ रैखिक खर्च करता है।

तो - आपके प्रश्न के लिए:

दूसरा दृष्टिकोण अधिक तेज़ होगा, लेकिन यह कम पठनीय और बनाए रखने के लिए कठिन है। जैसा कि मैंने कहा, आपके विशिष्ट मामले में आप शायद अंतर नहीं देखेंगे।


.Concat () के बारे में मत भूलना। मैं मूल पोस्ट उदाहरण की तरह छोटे तारों का उपयोग करते समय इसे 10 से 18 एमएस से कहीं भी होने के लिए बीता हुआ समय बताऊंगा।
Droo

9
जब आप इसके बारे में सही होते हैं +=, तो मूल उदाहरण एक अनुक्रम था +, जो संकलक एकल string.concatकॉल में बदल जाता है । आपके परिणाम लागू नहीं होते हैं।
ब्लाइंडी

1
@ बेलिंडी और ड्रू: - आप दोनों सही हैं। इस तरह के परिदृश्य में .concate सबसे अच्छा वर्कअराउंड है, क्योंकि हर बार लूप रूटीन निष्पादित होने पर + नई वस्तु बनाता है।
परिधि

3
क्या आप जानते हैं कि उसके तारे () को पाश में नहीं कहा जाता है?
ओमी यदान

मैंने इस उदाहरण की गति का परीक्षण करने की कोशिश की है। तो मेरे परिणाम हैं: धीमी गति से समाप्त 29672 एमएस; तेजी से बीते 15 मि। तो उत्तर स्पष्ट है। लेकिन अगर यह 100 पुनरावृत्तियों होगा - समय समान है - 0 एमएस। यदि 500 ​​पुनरावृत्तियों - 16 एमएस और 0 एमएस। और इसी तरह।
अर्नेस्टस ग्रोडिस

28

मैं इस तथ्य पर अपने बॉस से भिड़ गया था कि क्या परिशिष्ट या + का उपयोग किया जाए। जैसा कि वे परिशिष्ट का उपयोग कर रहे हैं (मैं अभी भी पता नहीं लगा सकता क्योंकि वे कहते हैं कि हर बार एक नई वस्तु बनाई जाती है)। इसलिए मैंने कुछ R & D करने के बारे में सोचा। हालांकि मुझे माइकल बोर्गवर्ड स्पष्टीकरण बहुत पसंद है, लेकिन अगर किसी को वास्तव में भविष्य में जानने की आवश्यकता होगी, तो मैं एक स्पष्टीकरण दिखाना चाहता हूं।

/**
 *
 * @author Perilbrain
 */
public class Appc {
    public Appc() {
        String x = "no name";
        x += "I have Added a name" + "We May need few more names" + Appc.this;
        x.concat(x);
        // x+=x.toString(); --It creates new StringBuilder object before concatenation so avoid if possible
        //System.out.println(x);
    }

    public void Sb() {
        StringBuilder sbb = new StringBuilder("no name");
        sbb.append("I have Added a name");
        sbb.append("We May need few more names");
        sbb.append(Appc.this);
        sbb.append(sbb.toString());
        // System.out.println(sbb.toString());
    }
}

और उपरोक्त वर्ग के डिसएस्पेशन के रूप में सामने आता है

 .method public <init>()V //public Appc()
  .limit stack 2
  .limit locals 2
met001_begin:                                  ; DATA XREF: met001_slot000i
  .line 12
    aload_0 ; met001_slot000
    invokespecial java/lang/Object.<init>()V
  .line 13
    ldc "no name"
    astore_1 ; met001_slot001
  .line 14

met001_7:                                      ; DATA XREF: met001_slot001i
    new java/lang/StringBuilder //1st object of SB
    dup
    invokespecial java/lang/StringBuilder.<init>()V
    aload_1 ; met001_slot001
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    ldc "I have Added a nameWe May need few more names"
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    aload_0 ; met001_slot000
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/Object;)Ljava/lan\
g/StringBuilder;
    invokevirtual java/lang/StringBuilder.toString()Ljava/lang/String;
    astore_1 ; met001_slot001
  .line 15
    aload_1 ; met001_slot001
    aload_1 ; met001_slot001
    invokevirtual java/lang/String.concat(Ljava/lang/String;)Ljava/lang/Strin\
g;
    pop
  .line 18
    return //no more SB created
met001_end:                                    ; DATA XREF: met001_slot000i ...

; ===========================================================================

;met001_slot000                                ; DATA XREF: <init>r ...
    .var 0 is this LAppc; from met001_begin to met001_end
;met001_slot001                                ; DATA XREF: <init>+6w ...
    .var 1 is x Ljava/lang/String; from met001_7 to met001_end
  .end method
;44-1=44
; ---------------------------------------------------------------------------


; Segment type: Pure code
  .method public Sb()V //public void Sb
  .limit stack 3
  .limit locals 2
met002_begin:                                  ; DATA XREF: met002_slot000i
  .line 21
    new java/lang/StringBuilder
    dup
    ldc "no name"
    invokespecial java/lang/StringBuilder.<init>(Ljava/lang/String;)V
    astore_1 ; met002_slot001
  .line 22

met002_10:                                     ; DATA XREF: met002_slot001i
    aload_1 ; met002_slot001
    ldc "I have Added a name"
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    pop
  .line 23
    aload_1 ; met002_slot001
    ldc "We May need few more names"
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    pop
  .line 24
    aload_1 ; met002_slot001
    aload_0 ; met002_slot000
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/Object;)Ljava/lan\
g/StringBuilder;
    pop
  .line 25
    aload_1 ; met002_slot001
    aload_1 ; met002_slot001
    invokevirtual java/lang/StringBuilder.toString()Ljava/lang/String;
    invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
    pop
  .line 28
    return
met002_end:                                    ; DATA XREF: met002_slot000i ...


;met002_slot000                                ; DATA XREF: Sb+25r
    .var 0 is this LAppc; from met002_begin to met002_end
;met002_slot001                                ; DATA XREF: Sb+9w ...
    .var 1 is sbb Ljava/lang/StringBuilder; from met002_10 to met002_end
  .end method
;96-49=48
; ---------------------------------------------------------------------------

उपरोक्त दो कोड से आप देख सकते हैं कि माइकल सही है। प्रत्येक मामले में केवल एक एसबी ऑब्जेक्ट बनाया जाता है।


27

जावा 1.5 के बाद से, "+" और StringBuilder.append () के साथ सरल एक लाइन का संयोजन बिलकुल वैसा ही है, जैसे कि केटकोड।

तो कोड पठनीयता के लिए, "+" का उपयोग करें।

2 अपवाद:

  • बहुआयामी वातावरण: स्ट्रिंगर
  • छोरों में संघनन: स्ट्रिंगब्रुअर्ट / स्ट्रिंगबफ़र

22

जावा (1.8) के नवीनतम संस्करण का उपयोग करके डिस्सैम्ड ( javap -c) कंपाइलर द्वारा पेश किए गए अनुकूलन को दर्शाता है। +साथ ही sb.append()बहुत समान कोड उत्पन्न करेगा। हालांकि, यह व्यवहार का निरीक्षण करने के लिए सार्थक होगा यदि हम +एक लूप में उपयोग कर रहे हैं ।

एक लूप में + का उपयोग करके तार जोड़ना

जावा:

public String myCatPlus(String[] vals) {
    String result = "";
    for (String val : vals) {
        result = result + val;
    }
    return result;
}

बाइटकोड :( forपाश अंश)

12: iload         5
14: iload         4
16: if_icmpge     51
19: aload_3
20: iload         5
22: aaload
23: astore        6
25: new           #3                  // class java/lang/StringBuilder
28: dup
29: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
32: aload_2
33: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36: aload         6
38: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
41: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
44: astore_2
45: iinc          5, 1
48: goto          12

Stringbuilder.append का उपयोग करके तार जोड़ना

जावा:

public String myCatSb(String[] vals) {
    StringBuilder sb = new StringBuilder();
    for(String val : vals) {
        sb.append(val);
    }
    return sb.toString();
}

बाइटकैडो :( forलूप अंश)

17: iload         5
19: iload         4
21: if_icmpge     43
24: aload_3
25: iload         5
27: aaload
28: astore        6
30: aload_2
31: aload         6
33: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36: pop
37: iinc          5, 1
40: goto          17
43: aload_2

हालांकि इसमें थोड़ा बहुत अंतर है। पहले मामले में, जहां +उपयोग किया गया था, StringBuilderलूप पुनरावृत्ति के लिए प्रत्येक के लिए नया बनाया गया है और उत्पन्न परिणाम एक toString()कॉल (29 41 के माध्यम से) करके संग्रहीत किया जाता है । तो आप मध्यवर्ती स्ट्रिंग्स उत्पन्न कर रहे हैं जो कि लूप +में ऑपरेटर का उपयोग करते समय आपकी वास्तव में आवश्यकता नहीं है for


1
क्या यह Oracle JDK या OpenJDK है?
क्रिस्टोफ रूसो

12

जावा 9 में संस्करण 1 तेज होना चाहिए क्योंकि यह invokedynamicकॉल में परिवर्तित हो जाता है । अधिक विवरण JEP-280 में पाया जा सकता है :

यह विचार संपूर्ण स्ट्रींगबर्ल एपेंड डांस को एक साधारण इनवॉकेन्डीमिक कॉल के साथ java.lang.invoke.StringConcatFactory में बदलने का है, जो कि सहमति की आवश्यकता के मूल्यों को स्वीकार करेगा।


9

प्रदर्शन के कारणों के लिए, +=( Stringसंघनन) का उपयोग हतोत्साहित किया जाता है। क्यों होता है इसका कारण: जावा Stringएक अपरिवर्तनीय है, हर बार जब एक नया संयोजन किया जाता है तो एक नया Stringबनाया जाता है (नए का पुराने स्ट्रिंग से पहले से अलग फिंगरप्रिंट होता है )। नए तार बनाना जीसी पर दबाव डालता है और कार्यक्रम को धीमा कर देता है: ऑब्जेक्ट निर्माण महंगा है।

नीचे दिए गए कोड को इसे एक ही समय में अधिक व्यावहारिक और स्पष्ट बनाना चाहिए।

public static void main(String[] args) 
{
    // warming up
    for(int i = 0; i < 100; i++)
        RandomStringUtils.randomAlphanumeric(1024);
    final StringBuilder appender = new StringBuilder();
    for(int i = 0; i < 100; i++)
        appender.append(RandomStringUtils.randomAlphanumeric(i));

    // testing
    for(int i = 1; i <= 10000; i*=10)
        test(i);
}

public static void test(final int howMany) 
{
    List<String> samples = new ArrayList<>(howMany);
    for(int i = 0; i < howMany; i++)
        samples.add(RandomStringUtils.randomAlphabetic(128));

    final StringBuilder builder = new StringBuilder();
    long start = System.nanoTime();
    for(String sample: samples)
        builder.append(sample);
    builder.toString();
    long elapsed = System.nanoTime() - start;
    System.out.printf("builder - %d - elapsed: %dus\n", howMany, elapsed / 1000);

    String accumulator = "";
    start = System.nanoTime();
    for(String sample: samples)
        accumulator += sample;
    elapsed = System.nanoTime() - start;
    System.out.printf("concatenation - %d - elapsed: %dus\n", howMany, elapsed / (int) 1e3);

    start = System.nanoTime();
    String newOne = null;
    for(String sample: samples)
        newOne = new String(sample);
    elapsed = System.nanoTime() - start;
    System.out.printf("creation - %d - elapsed: %dus\n\n", howMany, elapsed / 1000);
}

एक रन के लिए परिणाम नीचे दिए गए हैं।

builder - 1 - elapsed: 132us
concatenation - 1 - elapsed: 4us
creation - 1 - elapsed: 5us

builder - 10 - elapsed: 9us
concatenation - 10 - elapsed: 26us
creation - 10 - elapsed: 5us

builder - 100 - elapsed: 77us
concatenation - 100 - elapsed: 1669us
creation - 100 - elapsed: 43us

builder - 1000 - elapsed: 511us
concatenation - 1000 - elapsed: 111504us
creation - 1000 - elapsed: 282us

builder - 10000 - elapsed: 3364us 
concatenation - 10000 - elapsed: 5709793us
creation - 10000 - elapsed: 972us

1 निष्कर्ष के लिए परिणामों पर विचार नहीं (जेआईटी अभी तक अपना काम नहीं कर रहा था), यहां तक ​​कि 10 निष्कर्षों के लिए प्रदर्शन जुर्माना प्रासंगिक है; हजारों सहमति के लिए, अंतर बहुत बड़ा है।

इस बहुत ही त्वरित प्रयोग से सीखे गए सबक (उपरोक्त कोड के साथ आसानी से प्रतिलिपि प्रस्तुत करने योग्य): कभी +=भी एक साथ समवर्ती तारों का उपयोग न करें , यहां तक ​​कि बहुत ही बुनियादी मामलों में जहां कुछ निष्कर्षों की आवश्यकता होती है (जैसा कि कहा गया है, नए तार बनाना वैसे भी महंगा है और दबाव डालता है) जीसी)।


9

नीचे दिए गए उदाहरण देखें:

static final int MAX_ITERATIONS = 50000;
static final int CALC_AVG_EVERY = 10000;

public static void main(String[] args) {
    printBytecodeVersion();
    printJavaVersion();
    case1();//str.concat
    case2();//+=
    case3();//StringBuilder
}

static void case1() {
    System.out.println("[str1.concat(str2)]");
    List<Long> savedTimes = new ArrayList();
    long startTimeAll = System.currentTimeMillis();
    String str = "";
    for (int i = 0; i < MAX_ITERATIONS; i++) {
        long startTime = System.currentTimeMillis();
        str = str.concat(UUID.randomUUID() + "---");
        saveTime(savedTimes, startTime);
    }
    System.out.println("Created string of length:" + str.length() + " in " + (System.currentTimeMillis() - startTimeAll) + " ms");
}

static void case2() {
    System.out.println("[str1+=str2]");
    List<Long> savedTimes = new ArrayList();
    long startTimeAll = System.currentTimeMillis();
    String str = "";
    for (int i = 0; i < MAX_ITERATIONS; i++) {
        long startTime = System.currentTimeMillis();
        str += UUID.randomUUID() + "---";
        saveTime(savedTimes, startTime);
    }
    System.out.println("Created string of length:" + str.length() + " in " + (System.currentTimeMillis() - startTimeAll) + " ms");
}

static void case3() {
    System.out.println("[str1.append(str2)]");
    List<Long> savedTimes = new ArrayList();
    long startTimeAll = System.currentTimeMillis();
    StringBuilder str = new StringBuilder("");
    for (int i = 0; i < MAX_ITERATIONS; i++) {
        long startTime = System.currentTimeMillis();
        str.append(UUID.randomUUID() + "---");
        saveTime(savedTimes, startTime);
    }
    System.out.println("Created string of length:" + str.length() + " in " + (System.currentTimeMillis() - startTimeAll) + " ms");

}

static void saveTime(List<Long> executionTimes, long startTime) {
    executionTimes.add(System.currentTimeMillis() - startTime);
    if (executionTimes.size() % CALC_AVG_EVERY == 0) {
        out.println("average time for " + executionTimes.size() + " concatenations: "
                + NumberFormat.getInstance().format(executionTimes.stream().mapToLong(Long::longValue).average().orElseGet(() -> 0))
                + " ms avg");
        executionTimes.clear();
    }
}

आउटपुट:

जावा बाईटकोड संस्करण: 8
java.version: 1.8.0_144
[str1.concat (str2)]
औसत 10000 concatenations के लिए समय: 0.096 एमएस औसत
10000 concatenations के लिए औसत समय: 0.185 एमएस औसत
10000 concatenations के लिए औसत समय: 0.327 एमएस औसत
के लिए औसत समय 10000 concatenations: 0.501 एमएस औसत
10000 concatenations के लिए औसत समय: 0.656 एमएस औसत
1,950,000 में: निर्मित लंबाई की स्ट्रिंग 17745 एमएस
[str1 + = str2]
10000 concatenations के लिए औसत समय: 0.21 एमएस औसत
10000 concatenations के लिए औसत समय: 0.652 एमएस औसत
के लिए औसत समय १०००० संघनन: १.१२ ९ एमएस औसत
समय १०००० संघों के लिए औसत समय : १. ms२en एमएस औसत
10000 concatenations के लिए औसत समय: 2.302 एमएस औसत
में 1,950,000: लंबाई की स्ट्रिंग निर्मित 60,279 एमएस
[str1.append (str2)]
10000 concatenations के लिए औसत समय: 0.002 एमएस औसत
10000 concatenations के लिए औसत समय: 0.002 एमएस औसत
10000 concatenations के लिए औसत समय: 0.002 एमएस औसत
10000 concatenations के लिए औसत समय: 0.002 एमएस औसत
10000 concatenations के लिए औसत समय: 0.002 एमएस औसत
में 1,950,000: लंबाई की स्ट्रिंग बनाया गया 100 एमएस

जैसे-जैसे स्ट्रिंग की लंबाई बढ़ती जाती है, वैसे-वैसे समतलता का समय बढ़ता जाता है।
वह जहां StringBuilderनिश्चित रूप से आवश्यक है।
जैसा कि आप देखते हैं, संघनन:, UUID.randomUUID()+"---"वास्तव में समय को प्रभावित नहीं करता है।

पुनश्च: मुझे नहीं लगता कि जावा में स्ट्रिंगब्यूलर का उपयोग कब करना वास्तव में इसका एक डुप्लिकेट है।
यह सवाल बात करता है toString()जिसके बारे में अधिकांश बार विशाल तार के संघटन नहीं करते हैं।


2019 अपडेट

java8समय के बाद से , चीजें थोड़ी बदल गई हैं। ऐसा लगता है कि अब (java13), का संघटन समय +=व्यावहारिक रूप से समान है str.concat()। हालांकि StringBuilderसंघ का समय अभी भी स्थिर है । (अधिक वर्बोज़ आउटपुट जोड़ने के लिए उपरोक्त मूल पोस्ट को थोड़ा संपादित किया गया था)

java bytecode संस्करण: 13
java.version: 13.0.1
[str1.concat (str2)]
10000 समारोहों के लिए औसत समय: 0.047 ms औसत
समय 10000 के लिए औसतन समय: 0.1 ms avg
समय 10000 संघों के लिए: 0.17 ms औसत
औसत समय। 10000 संघनन: १०००० संघों के
लिए ०.२५५ एमएस एवीजी औसत समय: ०.३३६ एमएस
एवीजी लंबाई की रचना: १ ९ ५५०० ९ १४ ms एमएस में
[str1 + = str2]
१०००० संघों के लिए औसत समय ०.००३37 एमएस एवीजी औसत समय १०००० संघों के
लिए: ०.०97 ९ एमएस औसत
समय। १०००० संघनन: ०.२४ ९ एमएस औसत
समय १०००० संघों के लिए औसत समय: ०.२ ९ av एमएस औसत
10000
समारोहों के लिए औसत समय: 0.326 एमएस औसत लंबाई में बनाया गया स्ट्रिंग: 1991 में 10191 एमएस में
[str1.append (str2)]
औसतन 10000 समय के लिए औसत समय: 0.001 एमएस औसत
औसत अवधि 10000 के लिए: 0.001 एमएस औसत
औसत अवधि 10000 के लिए। ०.००१ एमएस एवीजी का
औसत समय ६००० संघों के
लिए: ०.००१ एमएस
एवीजी औसत समय १०००० संघों के लिए: ०.००१ एमएस एवीजी लंबाई का तार बनाया गया: १ ९ ५०००० ४३ एमएस में

वर्थ नोटिंग के साथ bytecode:8/java.version:13संयोजन की तुलना में भी अच्छा प्रदर्शन लाभ हैbytecode:8/java.version:8


यह स्वीकृत उत्तर होना चाहिए .. यह स्ट्रिंग स्ट्रीम के आकार पर निर्भर करता है जो
कॉन्कैट

@ user1428716 FYI करें: उन परिणामों के साथ अपडेट किए गए उत्तर java13अब अलग हैं। लेकिन मुझे लगता है कि मुख्य निष्कर्ष समान है।
Marinos An

7

Apache Commons-Lang में एक ToStringBuilder वर्ग है जो उपयोग करने के लिए सुपर आसान है। यह अपेंडिक्स-लॉजिक को संभालने के साथ-साथ यह भी बताता है कि आप किस तरह से चाहते हैं कि आपका स्ट्रींग दिखे।

public void toString() {
     ToStringBuilder tsb =  new ToStringBuilder(this);
     tsb.append("a", a);
     tsb.append("b", b)
     return tsb.toString();
}

ऐसा आउटपुट देगा जो दिखता है com.blah.YourClass@abc1321f[a=whatever, b=foo]

या जंजीर का उपयोग करके अधिक संघनित रूप में:

public void toString() {
     return new ToStringBuilder(this).append("a", a).append("b", b").toString();
}

या यदि आप कक्षा के प्रत्येक क्षेत्र को शामिल करने के लिए प्रतिबिंब का उपयोग करना चाहते हैं:

public String toString() {
    return ToStringBuilder.reflectionToString(this);
}

आप चाहें तो ToString के स्टाइल को भी कस्टमाइज़ कर सकते हैं।


4

संभव के रूप में पठनीय के रूप में .String विधि बनाओ!

मेरी पुस्तक में इसके लिए एकमात्र अपवाद यह है कि क्या आप मुझे साबित कर सकते हैं कि यह महत्वपूर्ण संसाधनों का उपभोग करता है :) (हाँ, इसका अर्थ है प्रोफाइलिंग)

यह भी ध्यान दें कि जावा 5 संकलक जावा के पुराने संस्करणों में उपयोग किए गए हस्तलिखित "स्ट्रिंगबफ़र" दृष्टिकोण की तुलना में तेजी से कोड उत्पन्न करता है। यदि आप "+" का उपयोग करते हैं तो यह और भविष्य में वृद्धि मुफ्त में आती है।


3

कुछ बहस लगती है कि क्या स्ट्रींगब्यूलर का उपयोग करना अभी भी वर्तमान संकलक के साथ आवश्यक है। इसलिए मैंने सोचा कि मैं अपने 2 सेंट का अनुभव दूंगा।

मेरे पास JDBC10k रिकॉर्ड का एक परिणाम सेट है (हाँ, मुझे एक बैच में उन सभी की आवश्यकता है।) + ऑपरेटर के उपयोग से मेरी मशीन पर लगभग 5 मिनट लगते हैं Java 1.8। उपयोग करना stringBuilder.append("")एक ही क्वेरी के लिए एक सेकंड से भी कम समय लेता है।

इसलिए अंतर बहुत बड़ा है। एक लूप के अंदर StringBuilderबहुत तेज है।


2
मुझे लगता है कि यह बहस लूप के बाहर इस्तेमाल करने के बारे में है। मुझे लगता है कि एक आम सहमति है जिसे आपको लूप के अंदर उपयोग करने की आवश्यकता है।
जॉर्डन

2

'+' का उपयोग करते हुए प्रदर्शन वार स्ट्रिंग संघनन महंगा है क्योंकि स्ट्रिंग्स को जावा में अपरिवर्तनीय होने के बाद से इसे स्ट्रिंग की पूरी नई प्रतिलिपि बनाना पड़ता है। यह विशेष भूमिका निभाता है यदि संघनन बहुत बार होता है, जैसे: एक लूप के अंदर। जब मेरी आईडिया यह बताती है कि मैं ऐसा करने का प्रयास कर रहा हूँ:

यहां छवि विवरण दर्ज करें

सामान्य नियम:

  • एकल स्ट्रिंग असाइनमेंट के भीतर, स्ट्रिंग कॉन्फैनेटेशन का उपयोग करना ठीक है।
  • यदि आप वर्ण डेटा का एक बड़ा ब्लॉक बनाने के लिए पाशन कर रहे हैं, तो StringBuffer के लिए जाएं।
  • एक स्ट्रिंग पर + = का उपयोग करना हमेशा एक स्ट्रिंगरबफ़र का उपयोग करने की तुलना में कम कुशल होने वाला है, इसलिए इसे चेतावनी की घंटी बजनी चाहिए - लेकिन कुछ मामलों में प्राप्त अनुकूलन पठनीयता के मुद्दों की तुलना में नगण्य होगा, इसलिए अपने सामान्य ज्ञान का उपयोग करें।

यहाँ इस विषय के आसपास एक अच्छा जॉन स्कीट ब्लॉग है


2
आपको कभी भी स्ट्रिंगर का उपयोग नहीं करना चाहिए जब तक कि आपको कई थ्रेड्स से सिंक्रनाइज़ एक्सेस की आवश्यकता न हो। अन्यथा StringBuilder पसंद करते हैं जो सिंक्रनाइज़ नहीं है और इस प्रकार कम ओवरहेड है।
अर्की डेर लोनी

1

क्या मैं यह इंगित कर सकता हूं कि यदि आप किसी संग्रह पर पुनरावृति करने जा रहे हैं और स्ट्रिंगबर्ल का उपयोग कर रहे हैं, तो आप अपाचे कॉमन्स लैंग और स्ट्रिंगिंगटिल्स.जॉइन () (अलग-अलग स्वादों में) की जांच कर सकते हैं ?

प्रदर्शन के बावजूद, यह आपको StringBuilders बनाने के लिए और मिलियन समय की तरह प्रतीत होने वाले लूप के लिए बचाएगा ।


1

यहाँ मैंने जावा 8 में जाँच की है

  • स्ट्रिंग संघनन का उपयोग करना
  • StringBuilder का उपयोग करना

    long time1 = System.currentTimeMillis();
    usingStringConcatenation(100000);
    System.out.println("usingStringConcatenation " + (System.currentTimeMillis() - time1) + " ms");
    
    time1 = System.currentTimeMillis();
    usingStringBuilder(100000);
    System.out.println("usingStringBuilder " + (System.currentTimeMillis() - time1) + " ms");
    
    
    private static void usingStringBuilder(int n)
    {
        StringBuilder str = new StringBuilder();
        for(int i=0;i<n;i++)
            str.append("myBigString");    
    }
    
    private static void usingStringConcatenation(int n)
    {
        String str = "";
        for(int i=0;i<n;i++)
            str+="myBigString";
    }

यह वास्तव में एक बुरा सपना है यदि आप बड़ी संख्या में तार के लिए स्ट्रिंग संयोजन का उपयोग कर रहे हैं।

usingStringConcatenation 29321 ms
usingStringBuilder 2 ms

-1

मुझे लगता है कि हमें StringBuilder एपेंड दृष्टिकोण के साथ जाना चाहिए। कारण है कि :

  1. स्ट्रिंग कॉनसेनेट हर बार एक नया स्ट्रिंग ऑब्जेक्ट बनाएगा (जैसा कि स्ट्रिंग अपरिवर्तनीय ऑब्जेक्ट है), इसलिए यह 3 ऑब्जेक्ट बनाएगा।

  2. स्ट्रिंग बिल्डर के साथ केवल एक ही वस्तु बनाई जाएगी [StringBuilder उत्परिवर्तनीय है] और आगे की स्ट्रिंग इसके साथ जुड़ जाती है।


यह उत्तर क्यों अस्वीकृत है? docs.oracle.com/javase/8/docs/api/java/util/stream/… - म्यूटेबल रिडक्शन
user1428716

-4

सरल स्ट्रिंग्स के लिए जैसे मैं उपयोग करना पसंद करता हूं

"string".concat("string").concat("string");

आदेश में, मैं कहूंगा कि स्ट्रिंग बनाने का पसंदीदा तरीका स्ट्रिंगब्रुइल, स्ट्रिंग # कॉन्कट () का उपयोग कर रहा है, फिर ओवरलोडेड + ऑपरेटर। StringBuilder एक महत्वपूर्ण प्रदर्शन वृद्धि है जब बड़े तार काम करते हैं जैसे कि + ऑपरेटर का उपयोग प्रदर्शन में बड़ी कमी है (स्ट्रिंग आकार में वृद्धि के रूप में तेजी से बड़ी कमी)। .Concat () का उपयोग करने के साथ एक समस्या यह है कि यह NullPointerException को फेंक सकता है।


9
जेएलएस '+' को स्ट्रिंगबर्ल में बदलने की अनुमति देता है, इसलिए कॉनैट () का उपयोग '+' से भी खराब होने की संभावना है, और सबसे अधिक संभावना है कि सभी जेवीएम ऐसा करते हैं या एक अधिक कुशल विकल्प का उपयोग करते हैं - वही सच होने की संभावना नहीं है जो आपके उदाहरण में कम से कम एक पूर्ण मध्यवर्ती स्ट्रिंग बनाना और छोड़ना होगा।
लॉरेंस डोल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.