2009-12-10 20 views
54

क्या कोई प्रदर्शन परीक्षण परिणाम पारंपरिक लूप बनाम इटरेटर की तुलना में पारंपरिक है, जबकि एक ऐरेलिस्ट, हैश मैप और अन्य संग्रहों की यात्रा करते समय?जावा में लूप बनाम इटरेटर/फोरैच के लिए परंपरागत प्रदर्शन

या बस मुझे लूप के लिए इटरेटर का उपयोग क्यों करना चाहिए या इसके विपरीत?

+2

ध्यान दें कि कारण एक लूप का कारण एक लिंक्ड सूची के साथ धीमा है, यह है कि प्रत्येक कॉल को 'i''' सूची के प्रमुख से प्राप्त होता है।मुझे यकीन है कि यह हर किसी के लिए सहज रूप से स्पष्ट है, लेकिन इसे समझने में मुझे एक मिनट लग गया। –

उत्तर

73

यह मानते हुए कि तुम क्या मतलब है:

// traditional for loop 
for (int i = 0; i < collection.size(); i++) { 
    T obj = collection.get(i); 
    // snip 
} 

// using iterator 
Iterator<T> iter = collection.iterator(); 
while (iter.hasNext()) { 
    T obj = iter.next(); 
    // snip 
} 

// using iterator internally (confirm it yourself using javap -c) 
for (T obj : collection) { 
    // snip 
} 

इटरेटर कोई रैंडम एक्सेस (जैसे TreeSet, HashMap, LinkedList) के साथ संग्रह के लिए तेजी से होता है। सरणी और ऐरेलिस्ट के लिए, प्रदर्शन अंतर नगण्य होना चाहिए।

संपादित करें: मेरा मानना ​​है कि माइक्रो-बेंचमार्किंग प्रारंभिक अनुकूलन की तरह, बहुत अधिक बुराई की जड़ है। लेकिन फिर, मुझे लगता है कि ऐसी छोटी सी चीजों के प्रभावों के लिए एक भावना महसूस करना अच्छा है। इसलिए मैं a small test हुई: एक LinkedList और 100,000 के साथ respecively

  • एक ArrayList से अधिक

    • पुनरावृति "यादृच्छिक" तार
    • उनकी लंबाई संक्षेप (सिर्फ कुछ है कि संकलक पूरे पाश दूर का अनुकूलन कर से बचने के लिए)
    • सभी 3 पाश शैलियों (इटरेटर, प्रत्येक के लिए, काउंटर के साथ के लिए)

    परिणाम का उपयोग कर LinkedList के साथ "काउंटर के साथ के लिए" सभी के लिए समान है, लेकिन कर रहे हैं। अन्य सभी पांचों ने पूरी सूची में फिर से शुरू करने के लिए 20 मिलीसेकंड से भी कम समय लिया। एक लिंकडलिस्ट पर list.get(i) का उपयोग करके 100,000 बार पूरा करने के लिए 2 मिनट (!) से अधिक (60,000 गुना धीमा) लिया गया। वाह! :) इसलिए यह एक इटरेटर (स्पष्ट रूप से या स्पष्ट रूप से प्रत्येक के लिए उपयोग कर) का उपयोग करना सबसे अच्छा है, खासकर अगर आपको नहीं पता कि आपके किस प्रकार के व्यवहार और सूची का आकार है।

  • +10

    आपका लिंक्डलिस्ट परिणाम दिखाता है कि जब आप ओ (एन) से ओ (एन^2) (या अधिक) –

    +0

    पर जाते हैं तो क्या होता है * सभी अन्य पांचों ने पूरी सूची में फिर से 20 मिलीसेकंड से कम समय लिया * जेवीएम मृत- कोड ऑप्टिमाइज़ेशन में लात मारी ... लिंक्डलिस्ट और ऐरेलिस्ट के पुनरावृत्ति के बीच का अंतर महत्वपूर्ण है (ArrayList के पक्ष में) – bestsss

    +0

    @bestsss no, यह निश्चित रूप से नहीं किया गया। मैंने 100,000 यादृच्छिक तार (वास्तव में यूयूआईडी) उत्पन्न किए हैं और लूप के बाद stdout पर मुद्रित की गई अपनी लंबाई को सारांशित किया है। निश्चित रूप से, यूयूआईडी की एक ही लंबाई होती है जो आउटपुट अनुमानित बनाती है, लेकिन संकलक स्मार्ट नहीं है। मानो या नहीं, लेकिन एक आधुनिक सीपीयू 20 एमएस में ऐसा कर सकता है। एक और परिप्रेक्ष्य देने के लिए: मेरे सीपीयू में प्रति कोर 4,000 बोगोइप्स हैं। तो हम प्रति अरब या लाखों प्रति एमएस के अरबों निर्देशों के बारे में बात कर रहे हैं। इस प्रकार, कई लाखों निर्देशों के साथ 100,000 से अधिक तारों को फिर से शुरू करना संभव है। अधिकांश डेवलपर्स की तुलना में सीपीयू तेज़ी से सोचते हैं :) – sfussenegger

    1

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

    संपादित करें: मैं अन्य उत्तरों से देखता हूं कि वास्तव में आपको (i) बनाम एक इटरेटर का उपयोग करने के बीच अंतर का मतलब है। मैंने मूल प्रश्न को इटरेटर का उपयोग करने के पुराने और नए तरीकों के बीच अंतर का मतलब लिया।

    स्वीकार्य उत्तर में उल्लिखित कारणों के लिए, विशेष रूप से List कक्षाओं के लिए प्राप्त करें (i) और अपने स्वयं के काउंटर को बनाए रखने का उपयोग करना एक अच्छा विचार नहीं है।

    4

    प्रदर्शन ज्यादातर मामलों में समान है।
    इटरेटर तरह से सभी सूची कार्यान्वयन कि RandomAccess (: LinkedList उदाहरण) को लागू नहीं करते के लिए बेहतर है:

    बहरहाल, जब भी एक कोड एक सूची प्राप्त करता है, और उस पर लूप होता है, वहाँ अच्छी तरह से ज्ञात मामला है।

    कारण यह है कि इन सूचियों के लिए, सूचकांक द्वारा तत्व तक पहुंच निरंतर समय संचालन नहीं है।

    तो आप इटरेटर को अधिक मजबूत (कार्यान्वयन विवरण के लिए) पर भी विचार कर सकते हैं।


    हमेशा के रूप में, प्रदर्शन को पठनीयता के मुद्दों को छिपाना नहीं चाहिए।
    Java5 foreach पाश :-)

    +0

    धन्यवाद लेकिन ArrayList के बारे में क्या? – Harish

    +1

    ArrayList RandomAccess लागू करता है, इसलिए list.get (i) तेज़ है। प्रदर्शन मतभेद काफी नगण्य होना चाहिए। – sfussenegger

    +0

    नोट: हालांकि मुझे नहीं पता कि जेडीके में लिंक्डलिस्ट इस तरह से लिखा गया है, तो लिंक्डलिस्ट कार्यान्वयन लिखना मुश्किल होगा, जहां पारंपरिक लूप पारंपरिक रूप से यादृच्छिक पहुंच के रूप में कार्य करेगा। सभी की आवश्यकता होगी कि आंतरिक सूचक को अंतिम तत्व में रखना होगा जहां अनुरोध करने के लिए यादृच्छिक पहुंच होगी। ऐसा लगता है कि इस तरह के एक छोटे से कार्यान्वयन की तरह कोड के इतने सारे टुकड़े तेज हो जाएंगे कि मैं छवि में छवि नहीं कर सकता हूं। – tster

    1

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

    1

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

    22

    पुनरावर्तक उपयोग करने के लिए पहला कारण स्पष्ट शुद्धता है। आप एक मैनुअल सूचकांक का उपयोग करते हैं, वहाँ बहुत अहानिकर बंद-एक करके त्रुटियों है कि आप केवल अगर आप बहुत करीब से देखो देख सकते हैं हो सकता है: आप 1 पर या 0 पर शुरू किया? क्या आपने length - 1 पर समाप्त किया था? क्या आपने < या <= का उपयोग किया था? यदि आप एक इटरेटर का उपयोग करते हैं, तो यह देखना बहुत आसान है कि यह वास्तव में पूरे सरणी को फिर से सक्रिय कर रहा है। "कहो कि आप क्या करते हैं, जो भी आप कहते हैं वह करो।"

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

    0

    क्या sfussenegger करने के लिए +1 कहा। एफवाईआई, चाहे आप एक स्पष्ट इटरेटर या एक अंतर्निहित (यानी प्रत्येक के लिए) का उपयोग करते हैं, प्रदर्शन प्रदर्शन नहीं करेंगे क्योंकि वे एक ही बाइट कोड को संकलित करते हैं।

    +0

    वे एक ही बाइट कोड में संकलित नहीं होते हैं। फॉरएच लूप एक पुनरावर्तनीय पर पुनरावृत्त होता है और सूची के माध्यम से पुनरावृत्त होता है। लिंक्ड सूची के लिए (i) विधि पहले नोड से शुरू होती है, सभी तरह से ट्रैवर्स करती है और ऑब्जेक्ट लौटाती है। इसलिए यदि आप प्रत्येक बार I = 1 से 5 का उपयोग कर रहे हैं तो यह शुरुआत से शुरू होता है। नीचे मेरा जवाब देखें। – mickeymoon

    +0

    मेरा उत्तर स्पष्ट रूप से एक इटरेटर का उपयोग करने के लिए तुलना कर रहा था, इंडेक्स चर का उपयोग कर इसे पारंपरिक रूप से लूप के साथ तुलना नहीं कर रहा था। http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.14.2 – erturne

    2

    मैं नहीं मानता कि

    for (T obj : collection) { 
    

    .size पाश के माध्यम से गणना करता है() हर बार और इसलिए है तेजी से

    for (int i = 0; i < collection.size(); i++) { 
    
    +2

    आसानी से '(int i = 0, l = संग्रह) के साथ आसानी से उपचार किया गया। आकार(); i

    +0

    पहला व्यक्ति संग्रह.इटरेटर() विधि को कॉल करके संग्रह इटरेटर प्राप्त करता है और फिर इटरेटर की अगली() और hasNext() विधि को कॉल करके पुनरावृत्त करता है। – mickeymoon

    1

    हाँ, यह संग्रह हैं, जिस पर एक फर्क पड़ता है LinkedList की तरह यादृच्छिक पहुंच नहीं है। एक लिंक्ड सूची आंतरिक रूप से अगली ओर इंगित नोड्स द्वारा लागू की जाती है (एक सिर नोड से शुरू)।

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

    for(int i = 0; i< list.size(); i++) { 
        list.get(i); //this starts everytime from the head node instead of previous node 
    } 
    

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

    for(Object item: list) { 
        //item element is obtained from the iterator's next method. 
    } 
    
    संबंधित मुद्दे