क्या एक स्ट्रिंग में स्ट्रिंग स्ट्रिंग का पुन: उपयोग करना बेहतर है?


101

मैंने StringBuilder के उपयोग के संबंध में एक प्रदर्शन से संबंधित प्रश्न किया है। एक बहुत लंबे लूप में मैं एक हेरफेर कर रहा हूं StringBuilderऔर इसे इस तरह से किसी अन्य विधि से पास कर रहा हूं :

for (loop condition) {
    StringBuilder sb = new StringBuilder();
    sb.append("some string");
    . . .
    sb.append(anotherString);
    . . .
    passToMethod(sb.toString());
}

क्या StringBuilderहर लूप चक्र पर झटपट एक अच्छा समाधान है? और निम्नलिखित की तरह बेहतर हटाने के बजाय कॉल कर रहा है?

StringBuilder sb = new StringBuilder();
for (loop condition) {
    sb.delete(0, sb.length);
    sb.append("some string");
    . . .
    sb.append(anotherString);
    . . .
    passToMethod(sb.toString());
}

जवाबों:


69

मेरे मिनी-बेंचमार्क में दूसरा वाला लगभग 25% तेज है।

public class ScratchPad {

    static String a;

    public static void main( String[] args ) throws Exception {
        long time = System.currentTimeMillis();
        for( int i = 0; i < 10000000; i++ ) {
            StringBuilder sb = new StringBuilder();
            sb.append( "someString" );
            sb.append( "someString2"+i );
            sb.append( "someStrin4g"+i );
            sb.append( "someStr5ing"+i );
            sb.append( "someSt7ring"+i );
            a = sb.toString();
        }
        System.out.println( System.currentTimeMillis()-time );
        time = System.currentTimeMillis();
        StringBuilder sb = new StringBuilder();
        for( int i = 0; i < 10000000; i++ ) {
            sb.delete( 0, sb.length() );
            sb.append( "someString" );
            sb.append( "someString2"+i );
            sb.append( "someStrin4g"+i );
            sb.append( "someStr5ing"+i );
            sb.append( "someSt7ring"+i );
            a = sb.toString();
        }
        System.out.println( System.currentTimeMillis()-time );
    }
}

परिणाम:

25265
17969

ध्यान दें कि यह JRE 1.6.0_07 के साथ है।


संपादन में जॉन स्कीट के विचारों के आधार पर, यहां 2 संस्करण हैं। हालांकि समान परिणाम।

public class ScratchPad {

    static String a;

    public static void main( String[] args ) throws Exception {
        long time = System.currentTimeMillis();
        StringBuilder sb = new StringBuilder();
        for( int i = 0; i < 10000000; i++ ) {
            sb.delete( 0, sb.length() );
            sb.append( "someString" );
            sb.append( "someString2" );
            sb.append( "someStrin4g" );
            sb.append( "someStr5ing" );
            sb.append( "someSt7ring" );
            a = sb.toString();
        }
        System.out.println( System.currentTimeMillis()-time );
        time = System.currentTimeMillis();
        for( int i = 0; i < 10000000; i++ ) {
            StringBuilder sb2 = new StringBuilder();
            sb2.append( "someString" );
            sb2.append( "someString2" );
            sb2.append( "someStrin4g" );
            sb2.append( "someStr5ing" );
            sb2.append( "someSt7ring" );
            a = sb2.toString();
        }
        System.out.println( System.currentTimeMillis()-time );
    }
}

परिणाम:

5016
7516

4
मैंने अपने उत्तर में यह बताने के लिए एक संपादन जोड़ा है कि ऐसा क्यों हो रहा है। मैं थोड़ी देर (45 मिनट) में अधिक ध्यान से देखूंगा। ध्यान दें कि एपेंड कॉल में कॉन्टैक्शन करने से स्ट्रींगबर्ल को पहली जगह में इस्तेमाल करने की बात कम हो जाती है :)
जॉन स्कीट

3
यह भी देखना दिलचस्प होगा कि यदि आप दो ब्लॉकों को उलट देते हैं तो क्या होता है - पहले टेस्ट के दौरान JIT अभी भी "वार्मिंग अप" स्ट्रिंगबर्ल है। यह अच्छी तरह से अप्रासंगिक हो सकता है, लेकिन कोशिश करने के लिए दिलचस्प है।
जॉन स्कीट

1
मैं अभी भी पहले संस्करण के साथ जाऊँगा क्योंकि यह क्लीनर है । लेकिन यह अच्छा है कि आपने वास्तव में बेंचमार्क किया है :) अगला सुझाव दिया गया बदलाव: निर्माणकर्ता में पारित उचित क्षमता के साथ # 1 प्रयास करें।
जॉन स्कीट

25
Sb.setLength (0) का उपयोग करें; इसके बजाय, यह StringBuilder की सामग्री को फिर से बनाने या ऑब्जेक्ट (.delete) का उपयोग करने के खिलाफ खाली करने का सबसे तेज़ तरीका है। ध्यान दें कि यह StringBuffer पर लागू नहीं होता है, इसकी संगणना जांच गति लाभ को कम करती है।
पी अर्रेह

1
अक्षम्य उत्तर। पी Arrayah और डेव जार्विस सही हैं। setLength (0) दूर और सबसे कुशल उत्तर है। StringBuilder एक चार सरणी द्वारा समर्थित है और यह परिवर्तनशील है। बिंदु पर .toString () कहा जाता है, चार सरणी को कॉपी किया जाता है और एक अपरिवर्तनीय स्ट्रिंग को वापस करने के लिए उपयोग किया जाता है। इस बिंदु पर, StringBuilder के उत्परिवर्ती बफर को पुन: उपयोग किया जा सकता है, बस सम्मिलन सूचक को शून्य में स्थानांतरित करके (.setLength (0) के माध्यम से)। sb.toString अभी तक एक और कॉपी (अपरिवर्तनीय चार सरणी) बनाता है, इसलिए प्रत्येक पुनरावृत्ति को .setLength (0) विधि के विरोध में दो बफ़र्स की आवश्यकता होती है, जिसमें केवल एक नए बफर प्रति लूप की आवश्यकता होती है।
क्रिस

25

ठोस कोड लिखने के दर्शन में अपने स्ट्रिंग के अंदर अपने स्ट्रिंगब्यूलर को लगाने के लिए हमेशा बेहतर होता है। इस तरह यह कोड के बाहर अपने उद्देश्य के लिए नहीं जाता है।

दूसरी बात StringBuilder में सबसे बड़ी तात्कालिकता यह लूप के चलने के दौरान इसे बड़ा होने से बचने के लिए एक प्रारंभिक आकार देने से आती है

for (loop condition) {
  StringBuilder sb = new StringBuilder(4096);
}

1
आप पूरी तरह से घुंघराले कोष्ठक के साथ पूरी तरह से गुंजाइश कर सकते हैं, इस तरह से आपके पास बाहर स्ट्रिंगर नहीं है।
इपगा

@ ईपगा: यह अभी भी लूप के बाहर है। हां, यह बाहरी दायरे को प्रदूषित नहीं करता है, लेकिन यह एक अप्राकृतिक तरीका है जिसमें एक प्रदर्शन सुधार के लिए कोड लिखा जाता है जिसे संदर्भ में सत्यापित नहीं किया गया है ।
जॉन स्कीट

या इससे भी बेहतर, पूरी बात को अपने तरीके से रखें। ;-) लेकिन मैं फिर से सुनता हूँ: संदर्भ।
एपीगा

सम मनमानी संख्या (4096) के बजाय अपेक्षित आकार के साथ बेहतर अभी तक आरंभ करें। आपका कोड एक स्ट्रिंग को लौटा सकता है जो कि 4096 के चार [[
जेडडीके

24

अभी भी अधिक तेज:

public class ScratchPad {

    private static String a;

    public static void main( String[] args ) throws Exception {
        long time = System.currentTimeMillis();
        StringBuilder sb = new StringBuilder( 128 );

        for( int i = 0; i < 10000000; i++ ) {
            // Resetting the string is faster than creating a new object.
            // Since this is a critical loop, every instruction counts.
            //
            sb.setLength( 0 );
            sb.append( "someString" );
            sb.append( "someString2" );
            sb.append( "someStrin4g" );
            sb.append( "someStr5ing" );
            sb.append( "someSt7ring" );
            setA( sb.toString() );
        }

        System.out.println( System.currentTimeMillis()-time );
    }

    private static void setA( String aString ) {
        a = aString;
    }
}

ठोस कोड लिखने के दर्शन में, विधि के आंतरिक कामकाज को उन वस्तुओं से छिपाया जाना चाहिए जो विधि का उपयोग करते हैं। इस प्रकार यह सिस्टम के दृष्टिकोण से कोई फर्क नहीं पड़ता है कि आप स्ट्रिंग के भीतर या पाश के बाहर स्ट्रिंगबर्ल को फिर से परिभाषित करते हैं या नहीं। चूंकि इसे लूप के बाहर घोषित करना तेज है, और यह कोड को पढ़ने के लिए अधिक जटिल नहीं बनाता है, फिर ऑब्जेक्ट को फिर से स्थापित करने के बजाय पुन: उपयोग करें।

यहां तक ​​कि अगर कोड अधिक जटिल था, और आप कुछ के लिए जानते थे कि वस्तु तात्कालिकता अड़चन थी, तो टिप्पणी करें।

इस जवाब के साथ तीन रन:

$ java ScratchPad
1567
$ java ScratchPad
1569
$ java ScratchPad
1570

अन्य जवाब के साथ तीन रन:

$ java ScratchPad2
1663
2231
$ java ScratchPad2
1656
2233
$ java ScratchPad2
1658
2242

हालांकि महत्वपूर्ण नहीं है, StringBuilderप्रारंभिक बफर आकार सेट करना एक छोटा लाभ देगा।


3
यह अब तक का सबसे अच्छा जवाब है। StringBuilder एक चार सरणी द्वारा समर्थित है और यह परिवर्तनशील है। बिंदु पर .toString () कहा जाता है, चार सरणी को कॉपी किया जाता है और एक अपरिवर्तनीय स्ट्रिंग को वापस करने के लिए उपयोग किया जाता है। इस बिंदु पर, StringBuilder के उत्परिवर्ती बफर को पुन: उपयोग किया जा सकता है, बस सम्मिलन सूचक को शून्य में स्थानांतरित करके (.setLength (0) के माध्यम से)। उन उत्तरों का सुझाव है कि एक नए ब्रांड स्ट्रिंगबर्ल प्रति लूप आवंटित करने के बारे में ऐसा प्रतीत नहीं होता है।
क्रिस

12

ठीक है, मुझे अब समझ में आ रहा है कि क्या चल रहा है, और इसका कोई मतलब नहीं है।

मैं इस धारणा के तहत था कि toStringबस char[]एक स्ट्रिंग निर्माता में अंतर्निहित पारित कर दिया, जो प्रतिलिपि नहीं लेता था । फिर एक कॉपी अगले "राइट" ऑपरेशन (जैसे delete) पर बनाई जाएगी । मेरा मानना ​​है कि पिछले संस्करण में ऐसा ही थाStringBuffer । (यह अभी नहीं है।) लेकिन नहीं - toStringबस सार्वजनिक Stringनिर्माणकर्ता को सरणी (और सूचकांक और लंबाई) पास करता है जो प्रतिलिपि लेता है।

तो "पुनः उपयोग StringBuilder" मामले में हम वास्तव में प्रति स्ट्रिंग डेटा की एक प्रति बनाते हैं, पूरे समय बफर में एक ही चार सरणी का उपयोग करते हुए। स्पष्ट रूप से StringBuilderहर बार एक नया निर्माण एक नया अंतर्निहित बफर बनाता है - और फिर उस बफर को कॉपी किया जाता है (कुछ हद तक, हमारे विशेष मामले में, लेकिन सुरक्षा कारणों से किया जाता है) जब एक नया स्ट्रिंग बनाते हैं।

यह सब दूसरे संस्करण के लिए निश्चित रूप से अधिक कुशल हो जाता है - लेकिन एक ही समय में मैं अभी भी कहूंगा कि यह बदसूरत कोड है।


.NET के बारे में कुछ मज़ेदार जानकारी, स्थिति अलग है। .NET स्ट्रिंगबर्नल आंतरिक रूप से नियमित रूप से "स्ट्रिंग" ऑब्जेक्ट को संशोधित करता है और स्ट्रिंग विधि केवल इसे लौटाता है (इसे गैर-परिवर्तनीय के रूप में चिह्नित करता है, इसलिए परिणामस्वरूप स्ट्रिंगबर्स्ट जोड़तोड़ इसे फिर से बनाएगा)। तो, ठेठ "नया StringBuilder-> इसे संशोधित करें-> स्ट्रिंग करने के लिए" अनुक्रम कोई अतिरिक्त प्रतिलिपि नहीं बनाएगा (केवल भंडारण का विस्तार करने या इसे सिकोड़ने के लिए, यदि परिणामस्वरूप स्ट्रिंग की लंबाई अपनी क्षमता से बहुत कम है)। जावा में यह चक्र हमेशा कम से कम एक प्रति (StringBuilder.toString ()) में बनाता है।
इवान डबरोव

सूर्य JDK पूर्व -1 में आपके द्वारा ग्रहण किए गए अनुकूलन थे: Bugs.sun.com/bugdatabase/view_bug.do?bug_id=6219959
Dan Berindei

9

चूंकि मुझे नहीं लगता कि यह अभी तक इंगित किया गया है, सन जावा संकलक में निर्मित अनुकूलन के कारण, जो स्वचालित रूप से स्ट्रिंग स्ट्रिंगर्स (StringBuffers पूर्व-J2SE 5.0) बनाता है जब यह स्ट्रिंग संघनन देखता है, तो सवाल में पहला उदाहरण इसके बराबर है:

for (loop condition) {
  String s = "some string";
  . . .
  s += anotherString;
  . . .
  passToMethod(s);
}

जो अधिक पठनीय है, आईएमओ, बेहतर दृष्टिकोण। अनुकूलन के आपके प्रयासों से कुछ प्लेटफ़ॉर्म में लाभ हो सकता है, लेकिन संभावित रूप से दूसरों को नुकसान हो सकता है।

लेकिन अगर आप वास्तव में प्रदर्शन के साथ मुद्दों में भाग रहे हैं, तो सुनिश्चित करें, दूर का अनुकूलन करें। मैं स्पष्ट रूप से स्ट्रिंग स्कीबर के बफर आकार को स्पष्ट करने के साथ शुरू करूँगा, हालांकि, जॉन स्कीट के अनुसार।


4

आधुनिक जेवीएम इस तरह से सामान के बारे में वास्तव में स्मार्ट है। मैं इसका दूसरा अनुमान नहीं लगाऊंगा और कुछ ऐसा कर सकता हूं जो कम बनाए रखने योग्य / पठनीय हो ... जब तक कि आप उत्पादन डेटा के साथ उचित बेंच मार्क नहीं करते हैं जो एक गैर-तुच्छ प्रदर्शन सुधार (और इसे दस्तावेज़ करता है) को मान्य करता है;


जहां "गैर-तुच्छ" कुंजी है - बेंचमार्क आनुपातिक रूप से तेजी से एक रूप दिखा सकता है , लेकिन वास्तविक ऐप में कितना समय लग रहा है, इस बारे में कोई संकेत नहीं है :)
जॉन स्कीट

नीचे मेरे उत्तर में बेंचमार्क देखें। दूसरा रास्ता तेज है।
ईपगा

1
@ ईपगा: आपका बेंचमार्क वास्तविक ऐप में प्रदर्शन में सुधार के बारे में बहुत कम कहता है, जहाँ स्ट्रिंगबर्ल आवंटन को करने में लगने वाला समय बाकी लूप की तुलना में मामूली हो सकता है। इसलिए बेंचमार्किंग में संदर्भ महत्वपूर्ण है।
जॉन स्कीट

1
@Epaga: जब तक वह इसे अपने वास्तविक कोड से नहीं मापता, तब तक हमें कोई सुराग नहीं होगा कि यह वास्तव में कितना महत्वपूर्ण है। यदि लूप के प्रत्येक पुनरावृत्ति के लिए बहुत सारे कोड हैं, तो मुझे दृढ़ता से संदेह है कि यह अभी भी अप्रासंगिक होगा। हम नहीं जानते कि "..." में क्या है
जॉन स्कीट

1
(मुझे गलत न समझें, btw - आपके बेंचमार्क परिणाम अभी भी अपने आप में बहुत दिलचस्प हैं। मैं माइक्रोबेनचर्च द्वारा मोहित हूं। मुझे वास्तविक जीवन परीक्षण करने से पहले अपने कोड को आकार से बाहर झुकना पसंद नहीं है।)
जॉन स्कीट

4

विंडोज पर सॉफ्टवेयर के विकास के साथ अपने अनुभव के आधार पर मैं कहूंगा कि अपने पाश के दौरान StringBuilder को साफ़ करना प्रत्येक पुनरावृत्ति के साथ एक StringBuilder को तुरंत चलाने से बेहतर प्रदर्शन है। इसे साफ़ करने से उस स्मृति को तुरंत अधिलेखित किया जा सकता है जिसके लिए कोई अतिरिक्त आवंटन आवश्यक नहीं है। मैं जावा कचरा संग्रहकर्ता के साथ पर्याप्त रूप से परिचित नहीं हूं, लेकिन मुझे लगता है कि मुक्त करने और कोई वसूली नहीं होती है (जब तक कि आपका अगला स्ट्रिंग स्ट्रांगबर्ल नहीं बढ़ता) तात्कालिकता से अधिक फायदेमंद है।

(मेरी राय हर किसी के सुझाव के विपरीत है। हम्म। इसे बेंचमार्क करने का समय।)


बात यह है कि अधिक मेमोरी को वैसे भी पुनः प्राप्त करना होगा, क्योंकि मौजूदा डेटा का उपयोग पिछले लूप पुनरावृत्ति के अंत में नव निर्मित स्ट्रिंग द्वारा किया जा रहा है।
जॉन स्कीट

ओह, यह समझ में आता है, हालांकि मेरे पास यह था कि स्ट्रिंग आवंटित कर रहा था और एक नया स्ट्रिंग इंस्टेंस वापस कर रहा था और बिल्डर के लिए बाइट बफर फिर से आवंटित करने के बजाय साफ़ कर रहा था।
cfeduke

एपागा के बेंचमार्क से पता चलता है कि क्लीयरिंग और री-यूजिंग हर पास पर तात्कालिकता का लाभ है।
cfeduke

1

L सेटलैक्लिफ्ट ’या improves डिलीट’ करने के कारण प्रदर्शन में सुधार होता है, ज्यादातर कोड doing लर्निंग ’बफर का सही आकार है, और मेमोरी आवंटन करने के लिए कम है। आमतौर पर, मैं संकलक को स्ट्रिंग अनुकूलन करने देने की सलाह देता हूं । हालाँकि, यदि प्रदर्शन महत्वपूर्ण है, तो मैं अक्सर बफर के अपेक्षित आकार की पूर्व-गणना करूँगा। डिफ़ॉल्ट StringBuilder आकार 16 वर्ण है। यदि आप उससे आगे बढ़ते हैं, तो उसे आकार बदलना होगा। आकार बदलने से प्रदर्शन खो रहा है। यहां एक और मिनी-बेंचमार्क है, जो इसे दिखाता है:

private void clear() throws Exception {
    long time = System.currentTimeMillis();
    int maxLength = 0;
    StringBuilder sb = new StringBuilder();

    for( int i = 0; i < 10000000; i++ ) {
        // Resetting the string is faster than creating a new object.
        // Since this is a critical loop, every instruction counts.
        //
        sb.setLength( 0 );
        sb.append( "someString" );
        sb.append( "someString2" ).append( i );
        sb.append( "someStrin4g" ).append( i );
        sb.append( "someStr5ing" ).append( i );
        sb.append( "someSt7ring" ).append( i );
        maxLength = Math.max(maxLength, sb.toString().length());
    }

    System.out.println(maxLength);
    System.out.println("Clear buffer: " + (System.currentTimeMillis()-time) );
}

private void preAllocate() throws Exception {
    long time = System.currentTimeMillis();
    int maxLength = 0;

    for( int i = 0; i < 10000000; i++ ) {
        StringBuilder sb = new StringBuilder(82);
        sb.append( "someString" );
        sb.append( "someString2" ).append( i );
        sb.append( "someStrin4g" ).append( i );
        sb.append( "someStr5ing" ).append( i );
        sb.append( "someSt7ring" ).append( i );
        maxLength = Math.max(maxLength, sb.toString().length());
    }

    System.out.println(maxLength);
    System.out.println("Pre allocate: " + (System.currentTimeMillis()-time) );
}

public void testBoth() throws Exception {
    for(int i = 0; i < 5; i++) {
        clear();
        preAllocate();
    }
}

परिणाम दिखाते हैं कि ऑब्जेक्ट का पुन: उपयोग अपेक्षित आकार का बफर बनाने की तुलना में लगभग 10% तेज है।


1

LOL, पहली बार जब मैंने कभी स्ट्रिंगरबुर्ल में स्ट्रिंग के संयोजन से प्रदर्शन की तुलना में लोगों को देखा। उस उद्देश्य के लिए, यदि आप "+" का उपयोग करते हैं, तो यह और भी तेज़ हो सकता है; डी। "स्थानीयता" की अवधारणा के रूप में पूरे स्ट्रिंग की पुनर्प्राप्ति के लिए गति देने के लिए स्ट्रिंगबर्ल का उपयोग करने का उद्देश्य।

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

कुछ लोगों ने कहा, प्लेन तेजी से उड़ता है। इसलिए, मैंने इसे अपनी बाइक से परीक्षण किया, और पाया कि विमान धीमी गति से चलता है। क्या आप जानते हैं कि मैंने प्रयोग सेटिंग्स कैसे निर्धारित की हैं?


1

काफी तेज नहीं है, लेकिन मेरे परीक्षणों से यह औसतन कुछ हद तक 1.6.0_45 64 बिट्स का उपयोग करके तेजी से युगल मिल दिखाता है: StringBuilder.setLength (0) के बजाय StringBuilder.delete () का उपयोग करें:

time = System.currentTimeMillis();
StringBuilder sb2 = new StringBuilder();
for (int i = 0; i < 10000000; i++) {
    sb2.append( "someString" );
    sb2.append( "someString2"+i );
    sb2.append( "someStrin4g"+i );
    sb2.append( "someStr5ing"+i );
    sb2.append( "someSt7ring"+i );
    a = sb2.toString();
    sb2.setLength(0);
}
System.out.println( System.currentTimeMillis()-time );

1

सबसे तेज़ तरीका है "सेटलैक्लिफ्ट" का उपयोग करना। इसमें नकल संचालन शामिल नहीं होगा। एक नया स्ट्रिंगबुल बनाने का तरीका पूरी तरह से बाहर होना चाहिए । StringBuilder.delete (int start, int end) के लिए धीमी गति है क्योंकि यह फिर से आकार देने वाले भाग के लिए सरणी को कॉपी करेगा।

 System.arraycopy(value, start+len, value, start, count-end);

उसके बाद, StringBuilder.delete () StringBuilder.count को नए आकार में अपडेट करेगा। जबकि StringBuilder.setLength () बस StringBuilder.count को नए आकार में अपडेट करने के लिए सरल करें ।


0

पहला मनुष्य के लिए बेहतर है। अगर कुछ JVM के कुछ संस्करणों पर दूसरा थोड़ा तेज है, तो क्या?

यदि प्रदर्शन महत्वपूर्ण है, तो StringBuilder को बायपास करें और अपना स्वयं का लिखें। यदि आप एक अच्छे प्रोग्रामर हैं, और इस बात का ध्यान रखें कि आपका ऐप इस फ़ंक्शन का उपयोग कैसे कर रहा है, तो आपको इसे और भी तेज़ बनाने में सक्षम होना चाहिए। सार्थक? शायद ऩही।

इस प्रश्न को "पसंदीदा प्रश्न" के रूप में क्यों देखा जाता है? क्योंकि प्रदर्शन अनुकूलन बहुत मजेदार है, भले ही यह व्यावहारिक हो या न हो।


यह केवल एक अकादमिक प्रश्न नहीं है। जबकि अधिकांश समय (95% पढ़ें) मैं पठनीयता और स्थिरता को पसंद करता हूं, वास्तव में ऐसे मामले हैं जो थोड़ा सुधार बड़े अंतर बनाता है ...
पियर लुइगी

ठीक है, मैं अपना जवाब बदल दूंगा। यदि कोई ऑब्जेक्ट एक विधि प्रदान करता है जो इसे साफ़ करने और पुन: उपयोग करने की अनुमति देता है, तो ऐसा करें। पहले कोड की जांच करें यदि आप सुनिश्चित करना चाहते हैं कि स्पष्ट कुशल है; शायद यह एक निजी सरणी जारी करता है! यदि कुशल है, तो लूप के बाहर की वस्तु को आवंटित करें और इसे अंदर पुन: उपयोग करें।
डोंगिलमोर

0

मुझे नहीं लगता कि यह इस तरह के प्रदर्शन को अनुकूलित करने की कोशिश करने के लिए साधुता है। आज (2019) दोनों मूर्तियाँ मेरे I5 लैपटॉप पर 100.000.000 लूप के लिए लगभग 11sec चल रही हैं:

    String a;
    StringBuilder sb = new StringBuilder();
    long time = 0;

    System.gc();
    time = System.currentTimeMillis();
    for (int i = 0; i < 100000000; i++) {
        StringBuilder sb3 = new StringBuilder();
        sb3.append("someString");
        sb3.append("someString2");
        sb3.append("someStrin4g");
        sb3.append("someStr5ing");
        sb3.append("someSt7ring");
        a = sb3.toString();
    }
    System.out.println(System.currentTimeMillis() - time);

    System.gc();
    time = System.currentTimeMillis();
    for (int i = 0; i < 100000000; i++) {
        sb.setLength(0);
        sb.delete(0, sb.length());
        sb.append("someString");
        sb.append("someString2");
        sb.append("someStrin4g");
        sb.append("someStr5ing");
        sb.append("someSt7ring");
        a = sb.toString();
    }
    System.out.println(System.currentTimeMillis() - time);

==> 11000 मिसे (लूप के अंदर घोषणा) और 8236 मिसे (लूप के बाहर की घोषणा)

भले ही आई विलम रनिंग प्रोग्राम्स फॉर अड्रेस डेडलिनेशन विथ कुछ बिलियन लूप्स ऑफ़ 2 सेकंड का अंतर है। 100 मिलियन छोरों के लिए कोई फर्क नहीं पड़ता क्योंकि यह कार्यक्रम घंटों तक चल रहा है। यह भी ध्यान रखें कि यदि आपके पास केवल एक परिशिष्ट विवरण है तो चीजें अलग-अलग हैं:

    System.gc();
    time = System.currentTimeMillis();
    for (int i = 0; i < 100000000; i++) {
        StringBuilder sb3 = new StringBuilder();
        sb3.append("someString");
            a = sb3.toString();
    }
    System.out.println(System.currentTimeMillis() - time);

    System.gc();
    time = System.currentTimeMillis();
    for (int i = 0; i < 100000000; i++) {
        sb.setLength(0);
        sb.delete(0, sb.length());
        sb.append("someString");
        a = sb.toString();
    }
    System.out.println(System.currentTimeMillis() - time);

==> 3416 मिसेक (लूप के अंदर), 3555 मिसे (लूप के बाहर) पहला स्टेटमेंट जो लूप के भीतर स्ट्रिंगबर्स्ट बना रहा है, उस मामले में तेज है। और, यदि आप निष्पादन के क्रम को बदलते हैं तो यह अधिक तेज होता है:

    System.gc();
    time = System.currentTimeMillis();
    for (int i = 0; i < 100000000; i++) {
        sb.setLength(0);
        sb.delete(0, sb.length());
        sb.append("someString");
        a = sb.toString();
    }
    System.out.println(System.currentTimeMillis() - time);

    System.gc();
    time = System.currentTimeMillis();
    for (int i = 0; i < 100000000; i++) {
        StringBuilder sb3 = new StringBuilder();
        sb3.append("someString");
            a = sb3.toString();
    }
    System.out.println(System.currentTimeMillis() - time);

==> 3638 मिसे (लूप के बाहर), 2908 मिसे (लूप के अंदर)

सादर, उलरिच


-2

एक बार घोषणा करें, और हर बार असाइन करें। यह एक अनुकूलन की तुलना में अधिक व्यावहारिक और पुन: प्रयोज्य अवधारणा है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.