StringBuilder का उपयोग कब करें?


83

मैं StringBuilder के लाभों को समझता हूं।

लेकिन अगर मैं 2 स्ट्रिंग्स को बदलना चाहता हूं, तो मैं मानता हूं कि स्ट्रांगबर्ल के बिना ऐसा करना बेहतर (तेज) है। क्या ये सही है?

किस बिंदु (स्ट्रिंग्स की संख्या) पर स्ट्रिंगबर्ल का उपयोग करना बेहतर हो जाता है?


1
मेरा मानना ​​है कि यह पहले कवर किया गया है।
मार्क शुलथिस


जवाबों:


79

मैं आपको जेफ एटवुड द्वारा माइक्रो-ऑप्टिमाइज़ेशन थिएटर की दुखद त्रासदी को पढ़ने का सुझाव देता हूं ।

यह सिंपल कॉनटेनेशन बनाम स्ट्रिंगबर्ल बनाम अन्य तरीकों से व्यवहार करता है।

अब, यदि आप कुछ संख्याएँ और रेखांकन देखना चाहते हैं, तो लिंक का अनुसरण करें;)


हाँ के लिए +1 ! इस बारे में चिंता करने में समय व्यतीत होता है, समय ऐसा नहीं है जो वास्तव में कुछ मायने रखता है।
ग्रेग डी

8
हालाँकि आपका पढ़ना गलत है: यह बहुत सारे मामलों में मायने नहीं रखता है, जब कोई भी लूपिंग शामिल नहीं है, अन्य मामलों में हालांकि यह बहुत मायने रख सकता है
पीटर

1
मैंने संपादन को हटा दिया है, क्योंकि यह एक स्वीकृत उत्तर में सिर्फ गलत जानकारी थी।
पीटर

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

2
यह स्वीकृत उत्तर क्यों है। मुझे नहीं लगता कि बस एक लिंक को छोड़ देना और यह कहना कि "यह पढ़ो" एक अच्छा जवाब है
कोलोब कैनियन

45

लेकिन अगर मैं 2 स्ट्रिंग्स को समेटना चाहता हूं, तो मैं मानता हूं कि स्ट्रांगबर्ल के बिना ऐसा करना बेहतर (तेज) है। क्या ये सही है?

यह वास्तव में सही है, आप पा सकते हैं कि वास्तव में बहुत अच्छी तरह से क्यों समझाया गया है:

http://www.yoda.arachsys.com/csharp/stringbuilder.html

संक्षेप: यदि आप एक बार में तार को समतल कर सकते हैं जैसे

var result = a + " " + b  + " " + c + ..

आप केवल प्रतिलिपि बनाई गई है (जिसके परिणामस्वरूप स्ट्रिंग की लंबाई पहले से गणना की जाती है) के लिए स्ट्रिंगबर्ल के बिना बेहतर है;

जैसी संरचना के लिए

var result = a;
result  += " ";
result  += b;
result  += " ";
result  += c;
..

हर बार नई वस्तुएं बनाई जाती हैं, इसलिए वहां आपको स्ट्रिंगबर्ल पर विचार करना चाहिए।

लेख के अंत में अंगूठे के इन नियमों को प्रस्तुत करता है:

अंगूठे का नियम

तो, आपको स्ट्रिंग स्ट्रिंग का उपयोग कब करना चाहिए, और आपको स्ट्रिंग संघनन ऑपरेटरों का उपयोग कब करना चाहिए?

  • निश्चित रूप से जब आप एक गैर-तुच्छ लूप में समाप्‍त कर रहे हैं, तो निश्चित रूप से StringBuilder का उपयोग करें - विशेष रूप से यदि आप निश्चित रूप से (संकलन समय पर) नहीं जानते हैं कि आप लूप के माध्यम से कितने पुनरावृत्तियों करेंगे। उदाहरण के लिए, किसी फ़ाइल को एक बार में एक चरित्र को पढ़ना, एक स्ट्रिंग का निर्माण करना, जैसा कि आप + = ऑपरेटर का उपयोग करके जाते हैं, संभावित प्रदर्शन आत्महत्या है।

  • जब आप कर सकते हैं (निश्चित रूप से) सब कुछ निर्दिष्ट करें जो एक बयान में संक्षिप्त करने की आवश्यकता है, तो निश्चित रूप से संघनन ऑपरेटर का उपयोग करें। (यदि आपके पास चीजों को संक्षिप्त करने के लिए है, तो String.Concat को स्पष्ट रूप से कॉल करने पर विचार करें - या String.Join यदि आपको एक सीमांकक की आवश्यकता है।)

  • शाब्दिक रूप से कई संक्षिप्त बिट्स को तोड़ने से डरो मत - परिणाम समान होगा। उदाहरण के लिए, प्रदर्शन के लिए कोई नुकसान नहीं होने के साथ, आप लंबी लाइनों को कई लाइनों में तोड़कर पठनीयता की सहायता कर सकते हैं।

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

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


14

System.String एक अपरिवर्तनीय वस्तु है - इसका मतलब है कि जब भी आप इसकी सामग्री को संशोधित करते हैं तो यह एक नई स्ट्रिंग आवंटित करेगा और इसमें समय (और मेमोरी?) लगता है। StringBuilder का उपयोग कर आप एक नया आवंटित किए बिना वस्तु की वास्तविक सामग्री को संशोधित करते हैं।

इसलिए स्ट्रिंग पर कई संशोधनों को करने की आवश्यकता होने पर स्ट्रिंगबर्ल का उपयोग करें।


8

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


1
यह गलत है। आपको StringBuilderकेवल तभी उपयोग करना चाहिए जब लूप या कंसट्रक्शन ऐनक के लिए एक प्रदर्शन समस्या है।
एलेक्स बैगनोलिनी

2
@ एलेक्स: यह हमेशा मामला नहीं है? ;) नहीं, गंभीरता से, मैं हमेशा एक पाश के अंदर संघनन के लिए StringBuilder का उपयोग करता हूं ... हालांकि, मेरे छोरों में 1k से अधिक पुनरावृत्तियां हैं ... @Binary: आम तौर पर, इसे संकलित किया जाना चाहिए string s = "abcd", कम से कम यह आखिरी बात है मैंने सुना है ... हालांकि, चर के साथ, यह सबसे अधिक संभावना है, कॉनकैट।
बॉबी

1
तथ्य यह है: यह लगभग हमेशा मामला नहीं है। मैंने हमेशा स्ट्रिंग ऑपरेटरों का इस्तेमाल किया a + "hello" + "somethingelse"और इसके बारे में कभी चिंता नहीं की। यदि यह एक समस्या बन जाएगी, तो मैं स्ट्रिंगबर्ल का उपयोग करूँगा। लेकिन मैंने इसके बारे में चिंता नहीं की, और इसे लिखने में कम समय बिताया।
एलेक्स बैगनोलिनी

3
बड़े तार के साथ पूरी तरह से कोई प्रदर्शन लाभ नहीं है - केवल कई संघनन के साथ।
कोनराड रूडोल्फ

1
@Konrad: क्या आप सुनिश्चित हैं कि कोई प्रदर्शन लाभ नहीं है? हर बार जब आप बड़े स्ट्रिंग को बदलते हैं, तो आप बड़ी मात्रा में डेटा कॉपी कर रहे हैं; हर बार जब आप छोटे तारों को समेटते हैं, तो आप केवल डेटा की थोड़ी मात्रा की नकल कर रहे हैं।
ल्यूक डे

6
  • यदि आप एक लूप में तारों को बदलते हैं, तो आपको नियमित स्ट्रिंग के बजाय स्ट्रिंगबर्ल का उपयोग करने पर विचार करना चाहिए
  • यदि यह एकल संगति है, तो आप निष्पादन के समय में अंतर बिल्कुल नहीं देख सकते हैं

इस बिंदु को साबित करने के लिए एक सरल परीक्षण ऐप है:

class Program
{
    static void Main(string[] args)
    {
        const int testLength = 30000;
        var StartTime = DateTime.Now;

        //TEST 1 - String
        StartTime = DateTime.Now;
        String tString = "test string";
        for (int i = 0; i < testLength; i++)
        {
            tString += i.ToString();
        }
        Console.WriteLine((DateTime.Now - StartTime).TotalMilliseconds.ToString());
        //result: 2000 ms

        //TEST 2 - StringBuilder
        StartTime = DateTime.Now;
        StringBuilder tSB = new StringBuilder("test string");
        for (int i = 0; i < testLength; i++)
        {
            tSB.Append(i.ToString());
        }
        Console.WriteLine((DateTime.Now - StartTime).TotalMilliseconds.ToString());
        //result: 4 ms

        Console.ReadLine();
    }
}

परिणाम:

  • 30'000 पुनरावृत्तियों

    • स्ट्रिंग - 2000 एमएस
    • स्ट्रिंगबर्ल - 4 एमएस
  • 1000 पुनरावृत्तियों

    • स्ट्रिंग - 2 एमएस
    • स्ट्रिंगबर्ल - 1 एमएस
  • 500 पुनरावृत्तियों

    • स्ट्रिंग - 0 एमएस
    • स्ट्रिंगबर्ल - 0 एमएस

5

विवरण बताने के लिए

तब तुम तीन को गिनोगे, और नहीं, कम नहीं। तीन हो तुम गिनती की संख्या हो जाएगा, और गिनती की संख्या तीन हो जाएगा। चार तो न गिनेंगे, न तुम दोनों को गिनोगे, सिवाय इसके कि तुम तीनों के पास जाओ। एक बार तीसरे नंबर पर होने के बाद, तीसरे नंबर पर पहुंचा जाए, तो एंटोबिच के अपने पवित्र हाथ ग्रेनेड को लॉबर्ब करें

मैं आम तौर पर कोड के किसी भी ब्लॉक के लिए स्ट्रिंग बिल्डर का उपयोग करता हूं जिसके परिणामस्वरूप तीन या अधिक स्ट्रिंग्स का संयोजन होता है।


यह निर्भर करता है: सामंजस्य केवल एक प्रति बनाता है: "रसेल" + "" + स्टीन + "।", केवल एक प्रति बनाएगा क्योंकि यह पहले से स्ट्रिंग की लंबाई को शांत करता है। केवल जब आपको अपने संघटन को विभाजित करना है, तो आपको एक बिल्डर के बारे में सोचना शुरू करना चाहिए
पीटर

4

कोई निश्चित जवाब नहीं है, केवल नियम-अंगूठे का। मेरे अपने व्यक्तिगत नियम कुछ इस तरह हैं:

  • यदि एक लूप में समाप्‍त हो रहा है, तो हमेशा ए का उपयोग करें StringBuilder
  • यदि तार बड़े हैं, तो हमेशा एक का उपयोग करें StringBuilder
  • यदि स्क्रीन पर कॉन्सेटेशन कोड सुव्यवस्थित और पठनीय है तो संभवतः ठीक है।
    यदि ऐसा नहीं है, तो एक का उपयोग करें StringBuilder

मुझे पता है कि यह एक पुराना विषय है, लेकिन मैं केवल सीखने को जानने वाला हूं और जानना चाहता हूं कि आप "लार्ज स्ट्रिंग" किसे मानते हैं?
मैथ्यू

4

लेकिन अगर मैं 2 स्ट्रिंग्स को बदलना चाहता हूं, तो मैं मानता हूं कि ऐसा करना बेहतर है और स्ट्रांगबर्ल के बिना ऐसा करना तेज है। क्या ये सही है?

हाँ। लेकिन इससे भी महत्वपूर्ण बात यह है कि ऐसी स्थितियों में वेनिला का उपयोग करना बहुत अधिक पठनीयString है। दूसरी तरफ, लूप में इसका उपयोग करना, समझ में आता है और यह कंसीनेशन के रूप में पठनीय भी हो सकता है।

मुझे अंगूठे के नियमों से सावधान रहना चाहिए जो एक थ्रेशोल्ड के रूप में विशिष्ट संख्या को समेटता है। लूप्स (और केवल लूप्स) में इसका उपयोग करना शायद उतना ही उपयोगी है, जितना कि याद रखना आसान है और अधिक समझ में आता है।


"मैं अंगूठे के नियमों से सावधान रहूंगा जो कि एक सीमा के रूप में विशिष्ट संख्या का हवाला देता है" <यह। इसके अलावा, सामान्य ज्ञान लागू होने के बाद, 6 महीने के समय में अपने कोड पर वापस आने वाले व्यक्ति के बारे में सोचें।
फिल कूपर

4

चूँकि इसके लिए एक स्पष्टीकरण खोजना मुश्किल है जो या तो राय से प्रभावित नहीं है या मेरे द्वारा खुद को परखने के लिए लिनक्यूपैड पर कोड की एक बिट लिखने के लिए सोचा गया था।

मैंने पाया कि i.ToString () परिवर्तन समय (छोटे छोरों में दिखाई) का उपयोग करने के बजाय छोटे आकार के तारों का उपयोग कर रहा है।

परीक्षण समझदारी से तुलनीय श्रेणियों में समय माप रखने के लिए पुनरावृत्तियों के विभिन्न अनुक्रमों का उपयोग करता है।

मैं अंत में कोड की नकल करूँगा ताकि आप इसे स्वयं आज़मा सकें (परिणाम.चर्चा ... डंप () LINQPad के बाहर काम नहीं करेगा)।

आउटपुट (एक्स-एक्सिस: परीक्षण किए गए पुनरावृत्तियों की संख्या, वाई-एक्सिस: टिक में लगने वाला समय):

Iterations अनुक्रम: 2, 3, 4, 5, 6, 7, 8, 9, 10 Iterations अनुक्रम: 2, 3, 4, 5, 6, 7, 8, 9, 10

Iterations अनुक्रम: 10, 20, 30, 40, 50, 60, 70, 80 Iterations अनुक्रम: 10, 20, 30, 40, 50, 60, 70, 80

Iterations अनुक्रम: 100, 200, 300, 400, 500 Iterations अनुक्रम: 100, 200, 300, 400, 500

कोड (LINQPad 5 का उपयोग करके लिखा गया है):

void Main()
{
    Test(2, 3, 4, 5, 6, 7, 8, 9, 10);
    Test(10, 20, 30, 40, 50, 60, 70, 80);
    Test(100, 200, 300, 400, 500);
}

void Test(params int[] iterationsCounts)
{
    $"Iterations sequence: {string.Join(", ", iterationsCounts)}".Dump();

    int testStringLength = 10;
    RandomStringGenerator.Setup(testStringLength);
    var sw = new System.Diagnostics.Stopwatch();
    var results = new Dictionary<int, TimeSpan[]>();

    // This call before starting to measure time removes initial overhead from first measurement
    RandomStringGenerator.GetRandomString(); 

    foreach (var iterationsCount in iterationsCounts)
    {
        TimeSpan elapsedForString, elapsedForSb;

        // string
        sw.Restart();
        var str = string.Empty;

        for (int i = 0; i < iterationsCount; i++)
        {
            str += RandomStringGenerator.GetRandomString();
        }

        sw.Stop();
        elapsedForString = sw.Elapsed;


        // string builder
        sw.Restart();
        var sb = new StringBuilder(string.Empty);

        for (int i = 0; i < iterationsCount; i++)
        {
            sb.Append(RandomStringGenerator.GetRandomString());
        }

        sw.Stop();
        elapsedForSb = sw.Elapsed;

        results.Add(iterationsCount, new TimeSpan[] { elapsedForString, elapsedForSb });
    }


    // Results
    results.Chart(r => r.Key)
    .AddYSeries(r => r.Value[0].Ticks, LINQPad.Util.SeriesType.Line, "String")
    .AddYSeries(r => r.Value[1].Ticks, LINQPad.Util.SeriesType.Line, "String Builder")
    .DumpInline();
}

static class RandomStringGenerator
{
    static Random r;
    static string[] strings;

    public static void Setup(int testStringLength)
    {
        r = new Random(DateTime.Now.Millisecond);

        strings = new string[10];
        for (int i = 0; i < strings.Length; i++)
        {
            strings[i] = Guid.NewGuid().ToString().Substring(0, testStringLength);
        }
    }

    public static string GetRandomString()
    {
        var indx = r.Next(0, strings.Length);
        return strings[indx];
    }
}

3

जब तक आप शारीरिक रूप से संघनन की संख्या (a + b + c ...) टाइप कर सकते हैं, तब तक यह एक बड़ा अंतर नहीं होना चाहिए। N वर्ग (N = 10 पर) एक 100X मंदी है, जो बहुत बुरा नहीं होना चाहिए।

बड़ी समस्या तब है जब आप सैंकड़ों तारों को समेट रहे हैं। N = 100 पर, आपको 10000X गुना मंदी मिलती है। जो काफी खराब है।


2

मुझे नहीं लगता कि कब उपयोग करना है या कब नहीं करना है। जब तक कि निश्चित रूप से किसी ने स्वर्ण स्थितियों के साथ बाहर आने के लिए कुछ व्यापक परीक्षण नहीं किए।

मेरे लिए, मैं StringBuilder का उपयोग नहीं करेगा अगर सिर्फ 2 विशाल तार कंक्रीटिंग। यदि एक अनिर्दिष्ट गणना के साथ लूप है, तो मुझे संभावना है, भले ही लूप छोटे मायने रखता हो।


वास्तव में यह स्ट्रिंग स्ट्रिंगब्यूअल को 2 स्ट्रिंग्स को समतल करने के लिए पूरी तरह से गलत होगा, लेकिन यह पूर्ण के साथ कुछ भी नहीं है। परीक्षण - जो गलत काम के लिए बस इसका उपयोग कर रहा है।
मार्क Gravell

1

एक एकल संघन एक StringBuilder का उपयोग करने के लायक नहीं है। मैंने आमतौर पर अंगूठे के एक नियम के रूप में 5 संघनन का उपयोग किया है।

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