2010-05-18 14 views
16

मैं सोच रहा था, size() विधि है कि आप मौजूदा ArrayList<T> कैश पर कॉल कर सकते हैं? या प्रदर्शन प्रदर्शन कोड में यह बेहतर है कि मैं स्थानीय int में size() स्टोर करता हूं?क्या ArrayList.size() विधि कैश्ड है?

मुझे उम्मीद है कि यह वास्तव में कैश किया जाएगा, जब आप size() पर कॉल के बीच आइटम नहीं जोड़ते/हटाते हैं।

क्या मैं सही हूँ?

अद्यतन
मैं इनलाइन किए जाने वाले या ऐसी बातों के बारे में बात नहीं कर रहा। मैं सिर्फ यह जानना चाहता हूं कि विधि size() स्वयं आंतरिक रूप से मूल्य को कैश करती है, या यह कहने पर हर बार गतिशील रूप से गणना करता है।

+0

यह ArrayList के लिए एक तेजी से कॉल है, लेकिन स्थानीय चर में परिणाम की दुकान करता है, तो आप एक परीक्षण के रूप में उपयोग प्रत्येक पाश पुनरावृत्ति में स्थिति। –

+1

क्या आप इस प्रश्न को स्पष्ट कर सकते हैं। ऐसा लगता है कि कुछ लोग इसे "आकार() का आकलन करते हैं या यह एक क्षेत्र को" v. "के रूप में छोटा रूप से वापस कर देता है, आकार के लिए कई कॉल() को एक कॉल के साथ आकार में बदल दिया जाएगा() बाद में इनवॉक्शंस को प्रतिस्थापित किया जा रहा है एक स्थानीय चर द्वारा जिसमें पिछले परिणाम सहेजा गया था "। मैंने उत्तरार्द्ध के रूप में व्याख्या की है, जबकि अन्य ने पूर्व के रूप में व्याख्या की है। कृपया इसे पूरी तरह से स्पष्ट करें। स्पष्टीकरण के लिए –

+0

धन्यवाद। –

उत्तर

11

मुझे नहीं लगता कि मैं कहूंगा कि यह "कैश्ड" है - लेकिन यह सिर्फ एक फ़ील्ड में संग्रहीत है, इसलिए यह अक्सर कॉल करने के लिए पर्याप्त तेज़ है।

size() का सूर्य JDK कार्यान्वयन बस है:

public int size() { 
    return size; 
} 
+0

@ जोन, लेकिन प्रदर्शन महत्वपूर्ण कोड में, यह फ़ंक्शन कॉल ओवरहेड के लिए बहुत अधिक भुगतान कर रहा है, जब मान समान होगा। –

+6

@ माइकल लेकिन जेआईटी ऐसी कॉल को इनलाइन करने के लिए पर्याप्त स्मार्ट है, इसलिए आपके पास फ़ंक्शन कॉल ओवरहेड नहीं है। – Jesper

+1

@ जेस्पर - मैं सराहना करता हूं कि कॉल को इनलाइन करने के लिए सैद्धांतिक रूप से संभव हो सकता है, लेकिन कौन सा वीएम वास्तव में ऐसा करता है? – msandiford

0

ऐरेलिस्ट का स्पष्ट कार्यान्वयन आंतरिक रूप से एक क्षेत्र में आकार को स्टोर करना होगा। अगर मुझे आकार बदलने के बाद भी गणना की जानी चाहिए तो मुझे आश्चर्य होगा।

0

इसकी आवश्यकता क्यों होगी? ArrayList एक सूची इंटरफ़ेस लागू करता है जिसे एक सरणी द्वारा समर्थित किया जाता है, आखिरकार।

मुझे लगता है कि यह सिर्फ size सदस्य है, जो आपके द्वारा चीजों को सम्मिलित करते समय बढ़ाया जाता है और जब आप हटाते हैं तो कमी हो जाती है, और फिर यह उसे वापस कर देता है।

मैंने अब एपीआई दस्तावेज़ों से अधिक नहीं देखा है, हालांकि।

+0

क्योंकि फ़ंक्शन आमंत्रण ओवरहेड गैर-लचीला है। आप केवल एक फ़ील्ड के मूल्य को वापस करने के लिए बहुत भुगतान कर रहे हैं। यदि इसे लूप में बार-बार बुलाया जा रहा है, तो आपको कॉल को eliding से कुछ बचत मिल सकती है। –

+0

@ माइकल जेआईटी इतनी सरल विधियों को इनलाइन करने के लिए पर्याप्त स्मार्ट है, इसलिए आपके पास फ़ंक्शन कॉल ओवरहेड नहीं है। – Jesper

+0

@ माइकल: निश्चित रूप से, लेकिन यह सवाल नहीं था कि सवाल क्या था। सवाल यह था कि क्या ऐरेलिस्ट * कैश * आकार है, ताकि आकार() केवल "इसे पुनः संयोजित" करने के बजाय कैश किए गए मान को वापस कर सके। मेरा जवाब यह बताने की कोशिश करता है कि गणना करने के लिए बहुत कुछ नहीं है, और अन्य स्रोत की जांच के बाद इसे वापस लेते हैं। विधि कॉल ओवरहेड के साथ इसका कोई लेना-देना नहीं है। – unwind

2

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

संपादित
हालांकि इस तरह के एक अनुकूलन एक जावा संकलक द्वारा नहीं किया जा सकता है, यह जिसे उपयुक्त है बताया गया है कि JIT ArrayList.size() के कार्यान्वयन इनलाइन कर सकते हैं ऐसी है कि यह केवल एक के बराबर होता है फ़ील्ड एक्सेस, बिना किसी अतिरिक्त विधि कॉल ओवरहेड के, इसलिए असल में लागत नगण्य है, हालांकि आप अभी भी एक अस्थायी रूप से सहेजकर थोड़ा बचा सकते हैं (जो संभावित रूप से मेमोरी लुकअप को खत्म कर सकता है और इसके बजाय एक सीपीयू रजिस्टर से परिवर्तनीय सेवा प्रदान कर सकता है)।

+0

डाउनवोट क्यों? –

+0

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

+0

@ माइकल - मुझे लगता है कि ऐसा इसलिए है क्योंकि हर कोई इस सवाल को "मूल रूप से संग्रहीत मूल्य या प्रत्येक बार गणना करता है" के रूप में व्याख्या कर रहा है और आप इसे इस रूप में व्याख्या कर रहे हैं "क्या आपको हर बार विधि कॉल करना होगा या उसमें संग्रहीत मूल्य होगा पहली कॉल के बाद एक रजिस्टर। " – tvanfosson

4

हां।

जावा स्रोत पर एक त्वरित नज़र आपको जवाब बताएगा।

/** 
* Returns the number of elements in this list. 
* 
* @return the number of elements in this list 
*/ 
public int size() { 
    return size; 
} 

तो यह रूप में अच्छा के रूप में एक विधि कॉल प्राप्त करने के लिए जा रहा है है:

+0

@ टिम, दिमाग इंगित करता है कि कोड कहां है? –

+0

@ माइकल - आपकी जेडीके स्थापना निर्देशिका में 'src.zip' फ़ाइल में। – Jesper

+0

क्षमा करें! मैंने धारणाएं की - मैं आम तौर पर अपने आईडीई के भीतर से जेडीके स्रोत तक पहुंच रखता हूं। हाथ के लिए धन्यवाद, जेस्पर। – Timothy

2

यह OpenJDK version में कार्यान्वयन है। यह बहुत संभावना नहीं है कि HotSpot इस विधि द्वारा लौटाए गए मान को कैश करता है, इसलिए यदि आप वास्तव में संबंधित संबंधित हैं, तो आप इसे स्वयं कैश कर सकते हैं। जब तक आपकी प्रोफाइलिंग ने यह नहीं दिखाया है कि यह एक बाधा है, हालांकि (बहुत संभावना नहीं है), आपको केवल एक क्षेत्र के मूल्य को वापस करने वाला एक साधारण विधि कॉल कैश किया गया है या नहीं, बल्कि आपको खुद को पठनीयता के साथ चिंता करनी चाहिए।

0

तो size() विधि का परिणाम कैशिंग काफ़ी का प्रभाव बढ़ेगा (जो यह कभी कभी होता है - मैं नियमित रूप से ArrayList.size() मेरी -Xprof उत्पादन में शीर्ष संकलित विधि के रूप में देखें) तो और भी अधिक speedup के लिए एक सरणी के लिए पूरी सूची परिवर्तित करने पर विचार ।

यहाँ एक चाल है कि यदि आप सूची पर पुनरावृति नियमित रूप से काम कर सकते हैं, लेकिन शायद ही कभी इसे अद्यतन:

class FooProcessor { 

    private Foo[] fooArray = null; 
    private List<Foo> fooList = new ArrayList<Foo>(); 

    public void addFoo(Foo foo) { 
     fooList.add(foo); 
     fooArray = null; 
    } 

    public void processAllFoos() { 
     Foo[] foos = getFooArray(); 
     for (int i = 0; i < foos.length; ++ i) { 
      process(foos[i]); 
     } 
    } 

    private void getFooArray() { 
     if (fooArray == null) { 
      Foo[] tmpArray = new Foo[fooList.size()]; 
      fooArray = fooList.toArray(tmpArray); 
     } 
     return fooArray; 
    } 

} 
संबंधित मुद्दे