सूची से अधिक पुनरावृत्ति इसके माध्यम से अनुक्रमण से अधिक तेज़ क्यों होगी?


125

एडीटी सूची के लिए जावा प्रलेखन पढ़ना यह कहता है:

सूची इंटरफ़ेस सूची तत्वों के लिए स्थितीय (अनुक्रमित) पहुंच के लिए चार तरीके प्रदान करता है। सूचियाँ (जैसे जावा सरणियाँ) शून्य आधारित हैं। ध्यान दें कि ये ऑपरेशन कुछ कार्यान्वयनों (उदाहरण के लिए लिंक्डलिस्ट क्लास) के लिए सूचकांक मूल्य के आनुपातिक समय में निष्पादित हो सकते हैं। इस प्रकार, किसी सूची में तत्वों पर पुनरावृत्ति करना आमतौर पर इसके माध्यम से अनुक्रमण करने के लिए बेहतर होता है यदि कॉलर को कार्यान्वयन का पता नहीं है।

इसका वास्तव में क्या मतलब है? जो निष्कर्ष निकाला गया है, मैं उसे नहीं समझता।


12
एक और उदाहरण जो आपको इस के सामान्य मामले को समझने में मदद कर सकता है, वह है जोएल स्पोल्स्की का लेख "बैक टू बेसिक्स" - "श्लेमील द पेंटरस एल्गोरिथम" की खोज।
एक CVn

जवाबों:


211

लिंक की गई सूची में, प्रत्येक तत्व में अगले तत्व के लिए एक संकेतक है:

head -> item1 -> item2 -> item3 -> etc.

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

इस प्रकार, यदि मैं प्रत्येक तत्व के मूल्य को प्रिंट करना चाहता हूं, अगर मैं यह लिखता हूं:

for(int i = 0; i < 4; i++) {
    System.out.println(list.get(i));
}

क्या होता है:

head -> print head
head -> item1 -> print item1
head -> item1 -> item2 -> print item2
head -> item1 -> item2 -> item3 print item3

यह बहुत ही अयोग्य है क्योंकि हर बार जब आप इसे सूचीबद्ध करते हैं तो यह सूची की शुरुआत से फिर से शुरू होता है और हर वस्तु से गुजरता है। इसका मतलब यह है कि आपकी जटिलता प्रभावी रूप से O(N^2)केवल सूची को पार करने के लिए है!

अगर इसके बजाय मैंने ऐसा किया:

for(String s: list) {
    System.out.println(s);
}

फिर क्या होता है:

head -> print head -> item1 -> print item1 -> item2 -> print item2 etc.

सभी एक ही त्रैमासिक में, जो है O(N)

अब, अन्य कार्यान्वयन के लिए जा रहा Listहै ArrayList, कि एक सरल सरणी द्वारा समर्थित है। उस मामले में उपरोक्त दोनों ट्रैवर्सल्स बराबर हैं, क्योंकि एक सरणी सन्निहित है इसलिए यह अनियंत्रित पदों पर यादृच्छिक कूदता है।


29
लघु सूचना: लिंक्डलिस्ट सूची के अंत से खोज करेगा यदि सूची के बाद के आधे हिस्से में सूचकांक है, लेकिन यह वास्तव में मौलिक अक्षमता को नहीं बदलता है। यह इसे केवल थोड़ा कम समस्याग्रस्त बनाता है।
जोकिम सॉयर

8
यह बहुत ही अयोग्य है । बड़े लिंक्डलिस्ट के लिए, छोटे के लिए, यह तेजी REVERSE_THRESHOLDसे काम कर सकता है 18 में सेट है java.util.Collections, यह टिप्पणी के बिना इतना उत्कीर्ण उत्तर देखने के लिए अजीब है।
बेस्टसीज

1
@DanDiplo, यदि संरचना लिंक की गई है, हाँ यह सच है। लिंक्डएस संरचनाओं का उपयोग करना, हालांकि, एक छोटा रहस्य है। वे लगभग हमेशा सरणी समर्थित वाले (अतिरिक्त मेमोरी फ़ुटप्रिंट, जीसी-अनफ्रीली और भयानक इलाके) की तुलना में बहुत खराब प्रदर्शन करते हैं। C # में मानक सूची सरणी समर्थित है।
bestsss

3
मामूली सूचना: यदि आप यह जांचना चाहते हैं कि किस प्रकार के पुनरावृत्ति प्रकार का उपयोग किया जाना चाहिए (अनुक्रमित बनाम Iterator / foreach), तो आप हमेशा यह जांच कर सकते हैं कि कोई सूची रैंडमList l = unknownList(); if (l instanceof RandomAccess) /* do indexed loop */ else /* use iterator/foreach */
असफल

1
@ KK_07k11A0585: वास्तव में आपके पहले उदाहरण में लूप के लिए संवर्धित को दूसरे उदाहरण की तरह एक पुनरावृत्त में संकलित किया गया है, इसलिए वे समकक्ष हैं।
ट्यूडर

35

जवाब यहाँ निहित है:

ध्यान दें कि ये ऑपरेशन कुछ कार्यान्वयनों के लिए सूचकांक मूल्य के अनुपात में समय पर निष्पादित हो सकते हैं (उदाहरण के लिए लिंक्डलिस्ट वर्ग)

एक लिंक की गई सूची में एक अंतर्निहित सूचकांक नहीं है; कॉलिंग के .get(x)लिए पहली प्रविष्टि खोजने के लिए सूची कार्यान्वयन की आवश्यकता होगी और .next()एक (O) (n) या रैखिक समय पहुंच के लिए x-1 बार कॉल करें , जहां एक सरणी-समर्थित सूची केवल backingarray[x]O (1) या निरंतर समय में अनुक्रमित कर सकती है ।

यदि आप JavaDoc के लिएLinkedList देखते हैं, तो आप टिप्पणी देखेंगे

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

जबकि JavaDoc के लिएArrayList इसी है

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

size, isEmpty, get, set, iterator, और listIteratorसंचालन निरंतर समय में चलाते हैं। जोड़ने का संचालन परिशोधित निरंतर समय में चलता है, अर्थात, n तत्वों को जोड़ने के लिए O (n) समय की आवश्यकता होती है। अन्य सभी ऑपरेशन रैखिक समय (मोटे तौर पर बोलने) में चलते हैं। LinkedListकार्यान्वयन के लिए निरंतर कारक कम है ।

"जावा कलेक्शंस फ्रेमवर्क के लिए बिग-ओ सारांश" नामक एक संबंधित प्रश्न का उत्तर इस संसाधन, "जावा कलेक्शंस JDK6" से मिलता है, जो आपको मददगार लग सकता है।


7

जबकि स्वीकृत उत्तर सबसे निश्चित रूप से सही है, क्या मैं एक मामूली दोष को इंगित कर सकता हूं। कोटेशन ट्यूडर:

अब, सूची के दूसरे कार्यान्वयन पर जा रहा है जो कि ArrayList है, जो कि एक साधारण सरणी द्वारा समर्थित है। उस मामले में उपरोक्त दोनों ट्रैवर्सल्स बराबर हैं , क्योंकि एक सरणी सन्निहित है इसलिए यह अनियंत्रित पदों पर यादृच्छिक कूदता है।

यह पूरी तरह सच नहीं है। सत्य यह है कि

एक ArrayList के साथ, एक हाथ से लिखा गिनती लूप लगभग 3x तेज है

स्रोत: प्रदर्शन के लिए डिजाइनिंग, Google का Android डॉक

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

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

मैं केवल यह बताना चाहता हूं कि एंड्रॉइड में विकास के संदर्भ में, ArrayLists के दोनों ट्रैवर्स आवश्यक नहीं हैं । सिर्फ विचार के लिए भोजन।


आपका स्रोत केवल Anndroid है। क्या यह अन्य जेवीएम के लिए भी है?
मत्स्येमन

पूरी तरह से निश्चित नहीं tbh, लेकिन फिर से, छोरों के लिए बढ़ाया का उपयोग ज्यादातर मामलों में डिफ़ॉल्ट होना चाहिए।
ध्रुव गरोला

यह मेरे लिए समझ में आता है, एक सरणी का उपयोग करने वाले डेटा संरचना तक पहुंचने पर सभी पुनरावृत्तियों के तर्क से छुटकारा पाना तेजी से काम करता है। मुझे नहीं पता कि क्या 3x तेज है, लेकिन निश्चित रूप से तेज।
सेटरेज़र 22

7

लुकअप के लिए एक ऑफसेट के साथ एक सूची iपर इरेटिंग , जैसे कि , पेंटर के एल्गोरिथ्म Shlemiel के अनुरूप है ।

शमील को सड़क चित्रकार के रूप में नौकरी मिलती है, बिंदीदार रेखाओं को सड़क के बीच में चित्रित करता है। पहले दिन वह सड़क पर पेंट की कैन ले जाता है और सड़क के 300 गज खत्म कर देता है। "वह बहुत बढिया है!" अपने मालिक का कहना है, "तुम एक तेज़ कार्यकर्ता हो!" और उसे एक कोपेक देता है।

अगले दिन शमीएल केवल 150 गज की दूरी पर हो जाता है। "ठीक है, यह लगभग कल जितना अच्छा नहीं है, लेकिन आप अभी भी एक तेज़ कार्यकर्ता हैं। 150 गज सम्मानजनक है," और उसे एक कोपेक देता है।

अगले दिन श्लेमीएल 30 गज की सड़क पर पेंट करता है। "केवल 30!" अपने मालिक को चिल्लाता है। "यह अस्वीकार्य है! पहले दिन आपने दस गुना ज्यादा काम किया! क्या चल रहा है?"

"मैं इसकी मदद नहीं कर सकता," Shlemiel कहते हैं। "हर दिन मैं पेंट के डिब्बे से दूर और दूर हो सकता हूं!"

स्रोत

इस छोटी सी कहानी से यह समझना आसान हो सकता है कि आंतरिक रूप से क्या चल रहा है और यह इतना अक्षम क्यों है।


4

LinkedListकार्यान्वयन के i-th तत्व को खोजने के लिए सभी तत्वों के माध्यम से i-th तक जाता है।

इसलिए

for(int i = 0; i < list.length ; i++ ) {
    Object something = list.get(i); //Slow for LinkedList
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.