1. संग्रह की सूची

जैसा कि आपको याद होगा, जावा में संग्रह हैं - एक ही प्रकार की वस्तुओं को संग्रहीत करने के लिए एक उपयोगी उपकरण।

आइए संग्रह से संबंधित मुख्य इंटरफेस को याद करने का प्रयास करें:

सूची , सेट , नक्शा और कतार

हमेशा की तरह, उपकरण आवश्यक रूप से अच्छे या बुरे नहीं होते हैं - क्या मायने रखता है कि आप उनका उपयोग उनके इच्छित उद्देश्य के लिए कर रहे हैं या नहीं। और ऐसा करने के लिए, हमें यह जानने के लिए उनकी विशिष्ट विशेषताओं को अच्छी तरह से समझना चाहिए कि किस संग्रह का उपयोग करना है और कब करना है।

1. सूची

आइए सबसे अधिक उपयोग किए जाने वाले संग्रह से शुरू करें।

एक सादे पुराने सरणी के जितना संभव हो उतना करीब सूचीबद्ध करें ।

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

2. सेट करें

सेट की पूरी तरह से अलग संरचना है।

सेट सबसे उपयुक्त होता है जब हमें अद्वितीय वस्तुओं को स्टोर करने की आवश्यकता होती है। उदाहरण के लिए, एक पुस्तकालय में लेखकों का एक समूह जहां प्रत्येक लेखक अद्वितीय होता है। लेकिन हम सीधे जाकर किसी विशिष्ट लेखक को उससे नहीं ले सकते। सेट हमें जल्दी से जांचने देता है कि क्या कोई विशेष लेखक हमारी लाइब्रेरी में मौजूद है, यानी हम यह जांच सकते हैं कि सेट में कोई अनूठी वस्तु मौजूद है या नहीं । हम प्रत्येक तत्व तक पहुँचने के लिए पूरे संग्रह पर पुनरावृति भी कर सकते हैं, लेकिन ऐसा करना इष्टतम नहीं है।

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

3. मानचित्र

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

मैप के लिए , इन रिश्तों को की-वैल्यू पेयर कहा जाता है।

हम अपने पुस्तकालय में इस संरचना का उपयोग लेखक की वस्तुओं को कुंजियों के रूप में और पुस्तकों की सूचियों ( वस्तुओं की सूची ) को मूल्यों के रूप में उपयोग करके कर सकते हैं। इस प्रकार, एक सेट की जाँच करने के बाद यह देखने के लिए कि क्या कोई लेखक वस्तु पुस्तकालय में मौजूद है, हम मानचित्र से उसकी पुस्तकों की सूची प्राप्त करने के लिए उसी लेखक वस्तु का उपयोग कर सकते हैं ।

4. कतार

कतार एक संग्रह है कि - आश्चर्य! - कतार के व्यवहार को लागू करता है। और कतार या तो LIFO (लास्ट इन, फ़र्स्ट आउट) या FIFO (फ़र्स्ट इन, फ़र्स्ट आउट) हो सकती है। क्या अधिक है, कतार द्विदिश, या "डबल-एंडेड" हो सकती है।

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

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

जैसा कि हम देख सकते हैं, इनमें से प्रत्येक संरचना अपने इच्छित उद्देश्य के लिए उपयोग किए जाने पर अच्छी है। और हमें एक ही पुस्तकालय उदाहरण में सभी चार प्रकार के संग्रहों के लिए अच्छा उपयोग मिला।

2. जटिलता

जैसा कि पहले ही उल्लेख किया गया है, हमने जिन संग्रहों पर विचार किया है वे इंटरफेस हैं, जिसका अर्थ है कि उनका उपयोग करने के लिए उनके पास कार्यान्वयन होना चाहिए।

जिस तरह सूक्ष्मदर्शी से कीलों को ठोंकना सबसे अच्छा विचार नहीं है, उसी तरह संग्रह का हर कार्यान्वयन हर स्थिति के अनुकूल नहीं होता है।

नौकरी के लिए सही उपकरण चुनते समय, हम आम तौर पर 2 विशेषताओं को देखते हैं:

  • टूल नौकरी के लिए कितना उपयुक्त है?
  • यह काम कितनी तेजी से पूरा करेगा?

हमने यह पता लगाने में कुछ समय बिताया है कि नौकरी के लिए उपयुक्त उपकरण कैसे चुनें, लेकिन इसकी गति कुछ नई है।

कंप्यूटिंग में, एक उपकरण की गति को अक्सर समय की जटिलता के रूप में व्यक्त किया जाता है और इसे बड़े अक्षर O द्वारा निरूपित किया जाता है।

दुनिया में समय जटिलता क्या है?

सरल शब्दों में, समय की जटिलता संग्रह में एक एल्गोरिथ्म के लिए आवश्यक समय को इंगित करता है कि एक विशेष क्रिया (तत्व को जोड़ना / हटाना, किसी तत्व की खोज करना) करने के लिए आवश्यक है।

ऐरेलिस्ट बनाम लिंक्डलिस्ट

आइए सूची इंटरफ़ेस के दो कार्यान्वयन - ArrayList और LinkedList का उपयोग करके इसे देखें ।

बाहरी दिखावे के लिए, इन संग्रहों के साथ काम करना समान है:

List<String> arrayList = new ArrayList<>();
arrayList.add(String);
arrayList.get(index);
arrayList.remove(index);
arrayList.remove(String);

List<String> linkedList = new LinkedList<>();

linkedList.add(String);

linkedList.get(index);
linkedList.remove(index);
linkedList.remove(String);

जैसा कि आप देख सकते हैं, दोनों संग्रह प्रकारों के लिए, तत्वों को जोड़ना, प्राप्त करना और निकालना समान दिखता है। ऐसा इसलिए है क्योंकि ये एक ही इंटरफ़ेस पर कार्यान्वयन हैं। लेकिन यहीं से समानताएं समाप्त हो जाती हैं।

सूची इंटरफ़ेस के उनके अलग-अलग कार्यान्वयन के कारण , ये दो संरचनाएँ दूसरों की तुलना में अलग-अलग कार्य अधिक कुशलता से करती हैं।

एक तत्व को हटाने और जोड़ने पर विचार करें।

यदि हमें ArrayList के मध्य से किसी तत्व को निकालने की आवश्यकता है , तो हमें उस सूची के किसी भी हिस्से को अधिलेखित करना होगा जिसे हम हटाते हैं।

मान लीजिए कि हमारे पास 5 तत्वों की एक सूची है और हम तीसरे को हटाना चाहते हैं।

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
list.remove(2);

इस मामले में, हटाने से एक सेल मुक्त हो जाता है, इसलिए हमें चौथा तत्व लिखना होगा जहां तीसरा था, और 5वां जहां चौथा था।

यह अत्यधिक अक्षम है।

सूची के मध्य में एक तत्व जोड़ने पर भी ऐसा ही होता है।

लिंक्डलिस्ट को अलग तरह से संरचित किया गया है। तत्वों को जोड़ना या हटाना तेज़ है, क्योंकि हमें केवल पिछले और अगले तत्वों में संदर्भों को बदलने की आवश्यकता है, जिससे उस वस्तु को बाहर रखा जा सके जिसे हम तत्वों की श्रृंखला से निकाल रहे हैं।

5 तत्वों की एक ही सूची के उदाहरण पर लौटते हुए, तीसरे तत्व को हटाने के बाद, हमें केवल दूसरे तत्व के संदर्भ को अगले तत्व और चौथे तत्व के संदर्भ को पिछले एक के संदर्भ में बदलना है।

जब किसी तत्व को सूची में जोड़ा जाता है, तो वही प्रक्रिया होती है, लेकिन विपरीत दिशा में।

ध्यान दें कि ArrayList की तुलना में हमें LinkedList में कितना कम काम करने की आवश्यकता है । और वह सिर्फ 5 तत्व हैं। यदि हमारी सूची में 100 या अधिक तत्व हैं, तो लिंक्डलिस्ट की श्रेष्ठता और भी अधिक ध्यान देने योग्य हो जाएगी।

लेकिन अगर हम किसी तत्व को इंडेक्स द्वारा एक्सेस करते हैं तो स्थिति कैसे बदलती है?

यहां सब ठीक उलटा है।

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

लेकिन एक लिंक्डलिस्ट बस उस तरह से काम नहीं करती है। एक निश्चित इंडेक्स वाले तत्व को खोजने के लिए हमें सूची के सभी तत्वों से गुजरना होगा।

क्या हम इन सभी को बड़े O के रूप में व्यक्त करने का प्रयास करेंगे?

आइए इंडेक्स द्वारा किसी तत्व को एक्सेस करके शुरू करें।

एक ArrayList में , यह एक चरण में होता है, चाहे तत्व सूची में कहीं भी स्थित हो। चाहे अंत में हो या शुरुआत में।

इस स्थिति में, समय जटिलता O(1) होगी ।

LinkedList में , हमें आवश्यक इंडेक्स के मूल्य के बराबर कई तत्वों पर पुनरावृति करनी होती है।

ऐसी कार्रवाई के लिए समय की जटिलता O(n) है , जहां n उस तत्व का सूचकांक है जिसकी हमें आवश्यकता है।

यहां आप देखते हैं कि हम बड़े-ओ कोष्ठकों में जो संख्या डालते हैं वह प्रदर्शन की गई क्रियाओं की संख्या से मेल खाती है।

शेल हम हटाने और जोड़ने के लिए वापस आते हैं?

आइए लिंक्डलिस्ट से शुरू करें।

क्योंकि हमें किसी तत्व को जोड़ने या हटाने के लिए बड़ी संख्या में क्रियाएं करने की आवश्यकता नहीं है, और इस ऑपरेशन की गति किसी भी तरह से इस बात पर निर्भर नहीं करती है कि तत्व कहाँ स्थित है, यह जटिलता O(1) के रूप में व्यक्त की जाती है और कहा जाता है स्थिर होना।

ArrayList के लिए इस ऑपरेशन की समय जटिलता भी O(n) है , जिसे हम रैखिक जटिलता कहते हैं।

रैखिक जटिलता वाले एल्गोरिदम में, चलने का समय संसाधित होने वाले तत्वों की संख्या पर सीधे निर्भर करता है। यह तत्व की स्थिति पर भी निर्भर हो सकता है, चाहे वह सूची की शुरुआत में हो या अंत में।

समय जटिलता लघुगणकीय भी हो सकती है। इसे O(log n) के रूप में व्यक्त किया जाता है ।

एक उदाहरण के रूप में, एक क्रमबद्ध ट्रीसेट पर विचार करें जिसमें 10 संख्याएँ हों। हम नंबर 2 खोजना चाहते हैं।

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

यहां एक सारणी है जो बाकी संग्रहों की समय जटिलता को सारांशित करती है।

इंडेक्स द्वारा कुंजी द्वारा खोज अंत में सम्मिलन अंत में निवेशन निष्कासन
सारणी सूची हे (1) लागू नहीं पर) हे (1) पर) पर)
लिंक्ड सूची पर) लागू नहीं पर) हे (1) हे (1) हे (1)
हैशसेट लागू नहीं हे (1) हे (1) लागू नहीं हे (1) हे (1)
ट्रीसेट लागू नहीं हे (1) ओ (लॉग एन) लागू नहीं ओ (लॉग एन) ओ (लॉग एन)
हैश मैप लागू नहीं हे (1) हे (1) लागू नहीं हे (1) हे (1)
ट्री-मैप लागू नहीं हे (1) ओ (लॉग एन) लागू नहीं ओ (लॉग एन) ओ (लॉग एन)
ऐरेडेक लागू नहीं लागू नहीं पर) हे (1) हे (1) हे (1)

अब जब हमारे पास लोकप्रिय संग्रहों की समय जटिलता दिखाने वाली एक तालिका है, तो हम इस सवाल का जवाब दे सकते हैं कि इतने सारे संग्रहों में से हम अक्सर ArrayList , HashSet और HashMap का उपयोग क्यों करते हैं ।

यह बस इतना है कि वे अधिकतर कार्यों के लिए सबसे कुशल हैं :)