Java 8 में, ArrayList की डिफ़ॉल्ट क्षमता अब शून्य क्यों है?


93

जैसा कि मुझे याद है, जावा 8 से पहले, डिफ़ॉल्ट क्षमता ArrayList10 थी।

आश्चर्यजनक रूप से, डिफ़ॉल्ट (शून्य) निर्माता पर टिप्पणी अभी भी कहती है: Constructs an empty list with an initial capacity of ten.

से ArrayList.java:

/**
 * Shared empty array instance used for default sized empty instances. We
 * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
 * first element is added.
 */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

...

/**
 * Constructs an empty list with an initial capacity of ten.
 */
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

जवाबों:


105

तकनीकी रूप से, यह 10शून्य नहीं है, यदि आप बैकिंग सरणी के एक आलसी प्रारंभिककरण के लिए स्वीकार करते हैं। देख:

public boolean add(E e) {
    ensureCapacityInternal(size + 1);
    elementData[size++] = e;
    return true;
}

private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }

    ensureExplicitCapacity(minCapacity);
}

कहाँ पे

/**
 * Default initial capacity.
 */
private static final int DEFAULT_CAPACITY = 10;

आप जिस चीज का जिक्र कर रहे हैं, वह केवल शून्य-आकार की प्रारंभिक सरणी वस्तु है जो सभी आरंभिक खाली ArrayListवस्तुओं के बीच साझा की गई है । यानी की क्षमता की 10गारंटी है आलसी , एक अनुकूलन जो जावा 7 में भी मौजूद है।

माना जाता है कि कंस्ट्रक्टर का अनुबंध पूरी तरह से सही नहीं है। शायद यह यहाँ भ्रम का स्रोत है।

पृष्ठभूमि

यहाँ माइक डुइगौ द्वारा एक ई-मेल है

मैंने खाली ArrayList और HashMap पैच का एक अद्यतन संस्करण पोस्ट किया है।

http://cr.openjdk.java.net/~mduigou/JDK-7143928/1/webrev/

यह संशोधित कार्यान्वयन किसी भी वर्ग के लिए नए क्षेत्रों का परिचय नहीं देता है । ArrayList के लिए बैकिंग सरणी का आलसी आवंटन केवल तभी होता है जब सूची डिफ़ॉल्ट आकार पर बनाई जाती है। हमारी प्रदर्शन विश्लेषण टीम के अनुसार, लगभग 85% ArrayList उदाहरण डिफ़ॉल्ट आकार पर बनाए गए हैं, इसलिए यह अनुकूलन भारी बहुमत के मामलों के लिए मान्य होगा।

HashMap के लिए, रचनात्मक उपयोग अनुरोधित प्रारंभिक आकार को ट्रैक करने के लिए थ्रेशोल्ड फ़ील्ड से बना है जब तक कि बाल्टी सरणी की आवश्यकता न हो। पढ़ने के पक्ष में खाली नक्शे के मामले को ismpty () के साथ जांचा जाता है। आकार लिखने पर (सारणी == EMPTY_TABLE) की तुलना बाल्टी सरणी को बढ़ाने की आवश्यकता का पता लगाने के लिए की जाती है। ReadObject में एक कुशल प्रारंभिक क्षमता चुनने की कोशिश करने के लिए थोड़ा और काम करना होगा।

प्रेषक: http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-April/015585.html


4
Bugs.java.com/bugdatabase/view_bug.do?bug_id=7143928 के अनुसार इससे ढेर का उपयोग कम होता है और प्रतिक्रिया समय में सुधार होता है (दो
आशंकाओं के

3
@khelwood: ArrayList वास्तव में अपनी क्षमता को "रिपोर्ट" नहीं करता है, इस Javadoc के अलावा अन्य: कोई getCapacity()विधि, या उसके बाद कुछ भी नहीं है। (उस ने कहा, कुछ ऐसा है जो ensureCapacity(7)एक डिफ़ॉल्ट-आरंभिक ऐरेलेस्ट के लिए एक नो-ऑप है, इसलिए मुझे लगता है कि हम वास्तव में कार्य करने वाले हैं क्योंकि इसकी प्रारंभिक क्षमता वास्तव में 10 थी।)
ruakh

10
अच्छी खुदाई। डिफ़ॉल्ट प्रारंभिक क्षमता वास्तव में शून्य नहीं है, लेकिन 10 है, डिफ़ॉल्ट मामले के साथ विशेष मामले के रूप में आलसी आवंटित किया गया है। आप इसका अवलोकन कर सकते हैं यदि आप बार-बार ArrayListनिर्माण करने वाले तत्वों को शून्य से जोड़कर निर्माणकर्ता को शून्य से जोड़ते हैं int, और यदि आप आंतरिक सरणी आकार को प्रतिबिंबित रूप से या डिबगर में देखते हैं। डिफ़ॉल्ट मामले में 1.5x की वृद्धि दर के बाद सरणी 0 से 10 तक, फिर 15 से 22 तक कूद जाती है। प्रारंभिक क्षमता के रूप में शून्य पास करने से 0 से 1, 2, 3, 4, 6, 9, 13, 19 .... का विकास होता है।
स्टुअर्ट मार्क्स

13
मैं माइक डुइगौ, परिवर्तन के लेखक और उद्धृत ईमेल हूं और मैं इस संदेश को स्वीकार करता हूं। 🙂 जैसा कि स्टुअर्ट कहते हैं कि प्रेरणा मुख्य रूप से प्रदर्शन के बजाय अंतरिक्ष की बचत के बारे में थी, हालांकि बैकिंग ऐरे के निर्माण से अक्सर बचने के कारण थोड़ा सा प्रदर्शन लाभ भी होता है।
माइक डुइगौ

4
@assylias:; ^) नहीं, यह अभी भी अपनी जगह है क्योंकि एक सिंगलटन emptyList()अभी भी कई खाली ArrayListउदाहरणों की तुलना में कम मेमोरी खाता है । यह अभी कम महत्वपूर्ण है और इस तरह से हर जगह की आवश्यकता नहीं है, विशेष रूप से उन स्थानों पर नहीं जहां बाद में तत्वों को जोड़ने की अधिक संभावना है। यह भी ध्यान रखें कि आप कभी-कभी एक अपरिवर्तनीय खाली सूची चाहते हैं और फिर emptyList()जाने का रास्ता है।
Holger

24

ArrayList की जावा 8 डिफ़ॉल्ट क्षमता में 0 है जब तक कि हम ArrayList ऑब्जेक्ट में कम से कम एक ऑब्जेक्ट नहीं जोड़ते (आप इसे आलसी इनिशियलाइज़ेशन कह सकते हैं)।

अब सवाल यह है कि यह बदलाव JAVA 8 में क्यों किया गया है?

उत्तर स्मृति की खपत को बचाना है। लाखों सरणी सूची ऑब्जेक्ट वास्तविक समय जावा अनुप्रयोगों में बनाए जाते हैं। 10 ऑब्जेक्ट्स के डिफ़ॉल्ट आकार का मतलब है कि हम निर्माण के दौरान अंतर्निहित सरणी के लिए 10 पॉइंटर्स (40 या 80 बाइट्स) आवंटित करते हैं और उन्हें खंभे के साथ भरते हैं। एक खाली सरणी (नल से भरा हुआ) बहुत सारी मेमोरी पर कब्जा कर लेता है।

आलसी आरंभीकरण इस मेमोरी की खपत को तब तक के लिए स्थगित कर देता है जब तक आप वास्तव में सरणी सूची का उपयोग नहीं करेंगे।

कृपया मदद के लिए नीचे दिए गए कोड देखें।

ArrayList al = new ArrayList();          //Size:  0, Capacity:  0
ArrayList al = new ArrayList(5);         //Size:  0, Capacity:  5
ArrayList al = new ArrayList(new ArrayList(5)); //Size:  0, Capacity:  0
al.add( "shailesh" );                    //Size:  1, Capacity: 10

public static void main( String[] args )
        throws Exception
    {
        ArrayList al = new ArrayList();
        getCapacity( al );
        al.add( "shailesh" );
        getCapacity( al );
    }

    static void getCapacity( ArrayList<?> l )
        throws Exception
    {
        Field dataField = ArrayList.class.getDeclaredField( "elementData" );
        dataField.setAccessible( true );
        System.out.format( "Size: %2d, Capacity: %2d%n", l.size(), ( (Object[]) dataField.get( l ) ).length );
}

Response: - 
Size:  0, Capacity:  0
Size:  1, Capacity: 10

जावा 8 में आर्टिलिस्ट की आर्टिकल डिफॉल्ट क्षमता इसे विवरण में बताती है।


7

यदि एक ArrayList के साथ किया गया पहला पहला ऑपरेशन addAllएक संग्रह को पास करना है जिसमें दस से अधिक तत्व हैं, तो ArrayList की सामग्री को रखने के लिए प्रारंभिक दस-तत्व सरणी बनाने में कोई भी प्रयास खिड़की से बाहर फेंक दिया जाएगा। जब भी किसी चीज़ को ArrayList में जोड़ा जाता है तो यह परीक्षण करना आवश्यक है कि परिणामी सूची का आकार बैकिंग स्टोर के आकार से अधिक होगा या नहीं; प्रारंभिक बैकिंग स्टोर को दस के बजाय आकार शून्य रखने की अनुमति देने से यह परीक्षण एक सूची के जीवनकाल में एक अतिरिक्त समय विफल हो जाएगा जिसका पहला ऑपरेशन एक "ऐड" है जिसे शुरुआती दस-आइटम सरणी बनाने की आवश्यकता होगी, लेकिन वह लागत है दस-आइटम सरणी बनाने की लागत से कम जो कभी समाप्त नहीं होती है।

कहा जा रहा है कि, कुछ संदर्भों में आगे प्रदर्शन में सुधार करना संभव हो सकता है यदि "AddAll" का अधिभार होता है जो निर्दिष्ट करता है कि वर्तमान एक के बाद सूची में कितने आइटम (यदि कोई हो) को जोड़ा जाएगा, और जो हो सकता है इसका आवंटन व्यवहार को प्रभावित करने के लिए उपयोग करें। कुछ मामलों में कोड जो अंतिम कुछ वस्तुओं को एक सूची में जोड़ता है, उनके पास एक बहुत अच्छा विचार होगा कि सूची को कभी भी उस स्थान से आगे बढ़ने की आवश्यकता नहीं है। ऐसी कई स्थितियाँ हैं जहाँ एक सूची को एक बार आबाद किया जाएगा और उसके बाद कभी संशोधित नहीं किया जाएगा। यदि बिंदु पर कोड पता है कि एक सूची का अंतिम आकार 170 तत्व होगा, तो इसमें 150 तत्व और आकार का बैकिंग स्टोर 160 है,


के बारे में बहुत अच्छे अंक addAll()। यह पहले मॉलॉक के आसपास दक्षता में सुधार के लिए एक और अवसर है।
केविनरपे

@kevinarpe: मैं चाहता हूं कि जावा की लाइब्रेरी ने कार्यक्रमों के लिए कुछ और तरीकों से इंजीनियर किया था कि कैसे चीजों का उपयोग करने की संभावना थी। उदाहरण के लिए, विकल्प की पुरानी शैली, कुछ उपयोग मामलों के लिए घटिया थी, लेकिन दूसरों के लिए उत्कृष्ट थी। अगर "प्रतिस्थापन के लिए अलग-अलग कार्य होते थे, जो मूल को मात देने की संभावना है" और "प्रतिस्थापन जो मूल को समाप्त करने की संभावना नहीं है", और कोड ने सही 90% समय का उपयोग किया, मुझे लगता है कि वे बहुत बेहतर रूप से या तो बेहतर प्रदर्शन कर सकते थे। पुराने या नए स्ट्रिंग कार्यान्वयन।
सुपरकैट

3

सवाल है? क्यों? ’।

स्मृति रूपरेखा निरीक्षण (उदाहरण के लिए ( https://www.yourkit.com/docs/java/help/inspections_mem.jsp#sparse_arrays) ) से पता चलता है कि खाली (नल से भरा) सरणियाँ मेमोरी पर कब्जा कर लेती हैं।

10 ऑब्जेक्ट्स के डिफ़ॉल्ट आकार का मतलब है कि हम निर्माण के दौरान अंतर्निहित सरणी के लिए 10 पॉइंटर्स (40 या 80 बाइट्स) आवंटित करते हैं और उन्हें खंभे के साथ भरते हैं। रियल जावा एप्लिकेशन लाखों ऐरे लिस्ट बनाते हैं।

शुरू किया गया संशोधन इस मेमोरी खपत को तब तक हटा देता है जब तक आप वास्तव में सरणी सूची का उपयोग नहीं करेंगे।


कृपया "कचरे" के साथ "उपभोग" को सही करें। आपके द्वारा प्रदत्त लिंक का अर्थ यह नहीं है कि वे हर जगह मेमोरी को टटोलना शुरू कर देते हैं, बस यह कि अशक्त तत्वों के साथ सरणियाँ, उनके लिए आवंटित की गई मेमोरी को बेकार में बर्बाद कर देती हैं। "उपभोग" का अर्थ है कि वे जादुई रूप से अपने आवंटन से परे स्मृति का उपयोग करते हैं, जो कि मामला नहीं है।
मेकेलिन

1

उपरोक्त प्रश्न के बाद मैं जावा 8 के ArrayList डॉक्यूमेंट के माध्यम से गया था। मैंने पाया कि डिफ़ॉल्ट आकार अभी भी केवल 10 है।

कृपया नीचे देखे


0

JAVA 8 में ArrayList का डिफ़ॉल्ट आकार stil 10 है। JAVA 8 में किया गया एकमात्र परिवर्तन यह है कि यदि कोई कोडर 10 से कम तत्वों को जोड़ता है, तो शेष सरणी सूची खाली स्थानों को शून्य करने के लिए निर्दिष्ट नहीं की जाती है। ऐसा इसलिए कह रहा हूं क्योंकि मैं खुद इस स्थिति से गुजर चुका हूं और ग्रहण ने मुझे JAVA 8 के इस बदलाव पर ध्यान दिया।

आप नीचे दिए गए स्क्रीनशॉट को देखकर इस बदलाव को सही ठहरा सकते हैं। इसमें आप देख सकते हैं कि ArrayList का आकार 10 के रूप में ऑब्जेक्ट [10] में निर्दिष्ट किया गया है, लेकिन प्रदर्शित तत्वों की संख्या केवल 7. बाकी शून्य मान तत्व यहां प्रदर्शित नहीं होते हैं। नीचे JAVA 7 में स्क्रीनशॉट केवल एक बदलाव के साथ समान है जो कि शून्य मान तत्व भी प्रदर्शित होते हैं जिसके लिए कोडर को अशक्त मानों को संभालने के लिए कोड लिखने की आवश्यकता होती है यदि वह संपूर्ण सरणी सूची में पुनरावृत्ति कर रहा है जबकि JAVA 8 में यह बोझ हटा दिया गया है कोडर / डेवलपर के प्रमुख।

स्क्रीन शॉट लिंक।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.