परिचय: स्ट्रिंग को जोड़ना सिर्फ "जोड़" नहीं है
\n\nकल्पना करें: आप C# में एक वेब सेवा लिख रहे हैं जो उपयोगकर्ता से टैग की सूची लेती है और उन्हें डेटाबेस में एक स्ट्रिंग के रूप में अल्पविराम से अलग करके भेजती है। या आप निर्यात के लिए CSV रिपोर्ट बना रहे हैं। या आप आईडी के सेट को लॉग कर रहे हैं। परिचित स्थिति? Stack Overflow पर "Creating a comma separated list from IList
यह कितना आसान लगता है? स्ट्रिंग की सूची लें, लूप चलाएं, अल्पविराम जोड़ें। लेकिन व्यवहार में, इस कार्य में कई छिपी चुनौतियाँ हैं: अंत में अतिरिक्त अल्पविराम से लेकर बड़े डेटा पर प्रदर्शन में भारी गिरावट तक। इस लेख में हम सिर्फ तैयार समाधान नहीं दिखाएंगे, बल्कि प्रत्येक की अंदरूनी कार्यप्रणाली को समझेंगे। आप जानेंगे कि string.Join, StringBuilder और यहां तक कि LINQ Aggregate कैसे काम करते हैं, और अपने कार्य के लिए सही उपकरण चुनना सीखेंगे।
चलिए शुरू करते हैं!
\n\nसमस्या: सरल प्रश्न, जटिल चुनौतियाँ
\n\nमान लीजिए, हमारे पास स्ट्रिंग का एक संग्रह है:
\n\nIEnumerable<string> fruits = new List<string> { "Apple", "Banana", "Cherry", "Date" };\n\nहम चाहते हैं: "Apple,Banana,Cherry,Date"। शुरुआती के दिमाग में सबसे पहले लूप आता है:
string result = "";\nbool first = true;\nforeach (var fruit in fruits)\n{\n if (!first)\n result += ",";\n result += fruit;\n first = false;\n}\n\nयह कोड काम करता है, लेकिन बहुत खराब तरीके से। क्यों? क्योंकि C# में स्ट्रिंग अपरिवर्तनीय (immutable) होती हैं। प्रत्येक += ऑपरेशन मेमोरी में एक नई स्ट्रिंग बनाता है, पिछली सामग्री को कॉपी करता है। 4 तत्वों पर यह ध्यान देने योग्य नहीं है, लेकिन 10,000 स्ट्रिंग पर आपको O(n²) की जटिलता और भारी मेमोरी खपत मिलेगी। अंदरूनी तौर पर, हज़ारों आवंटन और गार्बेज कलेक्शन होंगे।
स्पष्ट रूप से, एक बेहतर दृष्टिकोण की आवश्यकता है। और यहाँ मुख्य नायक मंच पर आते हैं।
\n\nविधि 1: string.Join — प्रदर्शन का राजा
\n\nसबसे सही और प्रदर्शनकारी तरीका स्टैटिक मेथड string.Join का उपयोग करना है:
string csv = string.Join(",", fruits);\nConsole.WriteLine(csv); // Apple,Banana,Cherry,Date\n\nकोड की एक पंक्ति — और काम हो गया। आइए string.Join ( .NET Core / .NET 5+ में) की अंदरूनी कार्यप्रणाली देखें।
अंदर क्या होता है?
\n- \n
- लंबाई की गणना: विधि पहले पूरे संग्रह (यदि यह
IEnumerable<T>है) पर जाती है ताकि कुल वर्णों की संख्या पता चल सके। यदि संग्रह एक सरणी याIListहै, तो यहCountके माध्यम से तुरंत लंबाई प्राप्त करता है। \n - बफर आवंटन: आवश्यक आकार का एक बड़ा वर्ण सरणी (
char[]) आवंटित किया जाता है: सभी स्ट्रिंग की लंबाई का योग + (स्ट्रिंग की संख्या - 1) * विभाजक की लंबाई। \n - कॉपी करना: सभी स्ट्रिंग और विभाजक बिना किसी अतिरिक्त आवंटन के इस बफर में कॉपी किए जाते हैं। \n
- स्ट्रिंग बनाना: बफर से एक अंतिम स्ट्रिंग बनाई जाती है। \n
परिणाम: एक मेमोरी आवंटन (अंतिम स्ट्रिंग के लिए) + अस्थायी बफर का आवंटन। कोई मध्यवर्ती स्ट्रिंग नहीं। जटिलता — सख्ती से O(n)।
\n\nIList<string> के लिए, विधि और भी तेज़ काम करती है, क्योंकि यह GetEnumerator को कॉल किए बिना, इंडेक्सर के माध्यम से तुरंत Count और प्रत्येक तत्व की लंबाई प्राप्त कर सकती है।
विधि 2: StringBuilder — पुराना दोस्त
\n\nअनुकूलित string.Join के आने से पहले (या यदि आप पुराने .NET Framework पर अटके हैं) मानक समाधान StringBuilder था:
var sb = new StringBuilder();\nbool first = true;\nforeach (var fruit in fruits)\n{\n if (!first)\n sb.Append(',');\n sb