.toArray (नया MyClass [0]) या .toArray (नया MyClass [myList.size ()])?


176

यह मानते हुए कि मेरे पास एक ArrayList है

ArrayList<MyClass> myList;

और मैं ऐरा को कॉल करना चाहता हूं, क्या उपयोग करने के लिए एक प्रदर्शन कारण है

MyClass[] arr = myList.toArray(new MyClass[myList.size()]);

ऊपर

MyClass[] arr = myList.toArray(new MyClass[0]);

?

मैं दूसरी शैली पसंद करता हूं, क्योंकि यह कम क्रिया है, और मैंने माना कि संकलक यह सुनिश्चित करेगा कि खाली सरणी वास्तव में बनाई नहीं जाती है, लेकिन मैं सोच रहा था कि क्या यह सच है।

बेशक, 99% मामलों में यह एक या दूसरे तरीके से फर्क नहीं करता है, लेकिन मैं अपने सामान्य कोड और मेरे अनुकूलित आंतरिक छोरों के बीच एक सुसंगत शैली रखना चाहूंगा ...


6
ऐसा लगता है कि अब एक नया ब्लॉग पोस्ट अलेक्सी शिपिलव, एरेस ऑफ़ विज़डम ऑफ़ द एनेट्स द्वारा प्रश्न को निपटाया गया है !
ग्लट्स

6
ब्लॉग पोस्ट से: "निचला रेखा: एअरे (नया टी [0]) तेज, सुरक्षित और अनुबंधित क्लीनर लगता है, और इसलिए अब डिफ़ॉल्ट विकल्प होना चाहिए।"
डेविड

जवाबों:


109

काउंटरपिनिटिवली, हॉटस्पॉट 8 पर सबसे तेज़ संस्करण है:

MyClass[] arr = myList.toArray(new MyClass[0]);

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

बेंचमार्क परिणाम (माइक्रोसेकंड में स्कोर, छोटा = बेहतर):

Benchmark                      (n)  Mode  Samples    Score   Error  Units
c.a.p.SO29378922.preSize         1  avgt       30    0.025  0.001  us/op
c.a.p.SO29378922.preSize       100  avgt       30    0.155  0.004  us/op
c.a.p.SO29378922.preSize      1000  avgt       30    1.512  0.031  us/op
c.a.p.SO29378922.preSize      5000  avgt       30    6.884  0.130  us/op
c.a.p.SO29378922.preSize     10000  avgt       30   13.147  0.199  us/op
c.a.p.SO29378922.preSize    100000  avgt       30  159.977  5.292  us/op
c.a.p.SO29378922.resize          1  avgt       30    0.019  0.000  us/op
c.a.p.SO29378922.resize        100  avgt       30    0.133  0.003  us/op
c.a.p.SO29378922.resize       1000  avgt       30    1.075  0.022  us/op
c.a.p.SO29378922.resize       5000  avgt       30    5.318  0.121  us/op
c.a.p.SO29378922.resize      10000  avgt       30   10.652  0.227  us/op
c.a.p.SO29378922.resize     100000  avgt       30  139.692  8.957  us/op

संदर्भ के लिए, कोड:

@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
public class SO29378922 {
  @Param({"1", "100", "1000", "5000", "10000", "100000"}) int n;
  private final List<Integer> list = new ArrayList<>();
  @Setup public void populateList() {
    for (int i = 0; i < n; i++) list.add(0);
  }
  @Benchmark public Integer[] preSize() {
    return list.toArray(new Integer[n]);
  }
  @Benchmark public Integer[] resize() {
    return list.toArray(new Integer[0]);
  }
}

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


2
बहुत ही रोचक टिप्पणी। मुझे आश्चर्य है कि किसी ने भी इस पर कोई टिप्पणी नहीं की। मुझे लगता है कि यह इसलिए है क्योंकि यह अन्य उत्तरों का विरोध करता है, जहाँ तक गति है। यह भी ध्यान रखना दिलचस्प है कि, इस लड़के की प्रतिष्ठा अन्य सभी उत्तरों (ers) की तुलना में लगभग अधिक है।
पिंप ट्राइज़किट

मैंने खुद को पीछे कर लिया। मैं इसके लिए बेंचमार्क भी देखना चाहूंगा MyClass[] arr = myList.stream().toArray(MyClass[]::new);.. जो मुझे लगता है कि धीमा होगा। इसके अलावा, मैं ऐरे डिक्लेरेशन के साथ अंतर के लिए बेंचमार्क देखना चाहूंगा। जैसे कि: MyClass[] arr = new MyClass[myList.size()]; arr = myList.toArray(arr);और MyClass[] arr = myList.toArray(new MyClass[myList.size()]);... में अंतर होना चाहिए या नहीं? मुझे लगता है कि ये दोनों एक ऐसा मुद्दा है जो toArrayफ़ंक्शन के बाहर होता है। लेकिन नमसते! मुझे नहीं लगता था कि मैं अन्य जटिल अंतरों के बारे में सीखूंगा।
पिंप ट्राइजकिट

1
@PimpTrizkit बस जाँच की गई: एक अतिरिक्त चर का उपयोग करने से उम्मीद के मुताबिक कोई फर्क नहीं पड़ता, एक धारा का उपयोग करने से toArrayसीधे कॉल करने में 60% और 100% अधिक समय लगता है (छोटे आकार, बड़े रिश्तेदार के ऊपर)
assylias

वाह, यह एक तेजी से प्रतिक्रिया थी! धन्यवाद! हाँ, मुझे शक था। एक धारा में परिवर्तित करना कुशल नहीं था। लेकिन आप कभी नहीं जान पाते!
पिंप ट्राइज़किट

2
यह वही निष्कर्ष यहां पाया गया था: shipilev.net/blog/2016/arrays-wisdom-ancients
user167019

122

Java 5 में ArrayList के रूप में , सरणी सही आकार (या बड़ा) होने पर पहले से ही भरी जाएगी। इसके फलस्वरूप

MyClass[] arr = myList.toArray(new MyClass[myList.size()]);

एक सरणी ऑब्जेक्ट बनाएगा, इसे भरें और इसे "गिरफ्तार" करें। दूसरी ओर

MyClass[] arr = myList.toArray(new MyClass[0]);

दो सरणियों का निर्माण करेगा। दूसरा एक लंबाई के साथ MyClass की एक सरणी है। 0. तो एक वस्तु के लिए एक ऑब्जेक्ट निर्माण है जिसे तुरंत फेंक दिया जाएगा। जहाँ तक स्रोत कोड का सुझाव है कि कंपाइलर / JIT इसको ऑप्टिमाइज़ नहीं कर सकता है ताकि यह निर्मित न हो। इसके अतिरिक्त, शून्य लंबाई वस्तु के परिणाम का उपयोग कर के रूप में कास्टिंग (ओं) के भीतर toArray () - विधि।

ArrayList.toArray का स्रोत देखें ():

public <T> T[] toArray(T[] a) {
    if (a.length < size)
        // Make a new array of a's runtime type, but my contents:
        return (T[]) Arrays.copyOf(elementData, size, a.getClass());
    System.arraycopy(elementData, 0, a, 0, size);
    if (a.length > size)
        a[size] = null;
    return a;
}

पहली विधि का उपयोग करें ताकि केवल एक ही वस्तु बनाई जाए और इससे बचें (निहित लेकिन फिर भी महंगी) कास्टिंग।


1
दो टिप्पणियाँ, किसी के लिए ब्याज की हो सकती हैं: 1) LinkedList.toArray (टी []) भी धीमी है (प्रतिबिंब का उपयोग करता है: Array.newInstance) और अधिक जटिल; 2) दूसरी ओर, JDK7 रिलीज़ में, मुझे यह जानकर बहुत आश्चर्य हुआ, कि आमतौर पर दर्द-मंद Array.newInstance लगभग सामान्य सरणी निर्माण के रूप में तेजी से प्रदर्शन करता है !
java.is.for.desktop

1
@ शकरिया आकार ArrayList का एक निजी सदस्य है, जो कि **** का विवरण देता है। देखें ArrayList Sourcecode
MyPasswordIsLasercats

3
बेंचमार्क के बिना प्रदर्शन का अनुमान केवल तुच्छ मामलों में काम करता है। वास्तव में, new Myclass[0]तेज है: shipilev.net/blog/2016/arrays-wisdom-ancients
Karol S


28

JetBrains Intellij Idea निरीक्षण से:

किसी संग्रह को एक सरणी में बदलने के लिए दो शैलियाँ हैं: या तो पूर्व-आकार की सरणी (जैसे c.toArray (नई स्ट्रिंग [c.size ()]) ) का उपयोग करके या किसी खाली सरणी (जैसे c.toArray (नई स्ट्रिंग ) का उपयोग करके) ०]

पूर्व-आकार के सरणी का उपयोग करने वाले पुराने जावा संस्करणों में सिफारिश की गई थी, क्योंकि उचित आकार की एक सरणी बनाने के लिए प्रतिबिंब कॉल आवश्यक था जो काफी धीमा था। हालाँकि, OpenJDK 6 के देर से अपडेट के बाद यह कॉल पूर्व-आकार वाले संस्करण की तुलना में खाली सरणी संस्करण के प्रदर्शन को और कभी-कभी और भी बेहतर बना देता था। साथ ही पूर्व-आकार की सरणी गुजरना समवर्ती या सिंक्रनाइज़ किए गए संग्रह के लिए खतरनाक है क्योंकि डेटा रेस आकार और Array कॉल के बीच संभव है, जिसके परिणामस्वरूप सरणी के अंत में अतिरिक्त नल हो सकते हैं, यदि संग्रह ऑपरेशन के दौरान समवर्ती रूप से सिकुड़ गया था।

यह निरीक्षण एक समान शैली का पालन करने की अनुमति देता है: या तो एक खाली सरणी (जिसे आधुनिक जावा में अनुशंसित किया गया है) का उपयोग करके या पूर्व-आकार के सरणी (जो पुराने जावा संस्करणों या तेज हॉटस्पॉट आधारित जेवीएम में तेज हो सकता है) का उपयोग कर सकता है।


यदि यह सब कॉपी / उद्धृत पाठ है, तो क्या हम इसे तदनुसार प्रारूपित कर सकते हैं और स्रोत का लिंक भी दे सकते हैं? मैं वास्तव में इंटेलीज निरीक्षण के कारण यहां आया था और मुझे उनके सभी निरीक्षणों और उनके पीछे के तर्क को देखने के लिए लिंक में बहुत दिलचस्पी है।
टिम ब्यूटे

3
यहाँ आप निरीक्षण ग्रंथों की जाँच कर सकते हैं: github.com/JetBrains/intellij-community/tree/master/plugins/…
Антон Антонов


17

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


2
अपवोटिंग 'खाली सरणी का पुन: उपयोग करें', क्योंकि यह पठनीयता और संभावित प्रदर्शन के बीच एक समझौता है जो विचार के योग्य है। पासिंग एक तर्क घोषित private static final MyClass[] EMPTY_MY_CLASS_ARRAY = new MyClass[0]प्रतिबिंब द्वारा निर्माण किया जा रहा से लौटे सरणी नहीं रोकता है, लेकिन यह है एक अतिरिक्त सरणी प्रत्येक हर बार निर्माण किया जा रहा रोकने के लिए।
माइकल शेपर

मैकहेल सही है, यदि आप एक शून्य-लंबाई सरणी का उपयोग करते हैं, तो कोई रास्ता नहीं है: (टी []] java.lang.reflect.Array.newInstance (a.getClass ()। GetComponentType (), आकार); जो आकार में होगा तो बहुत ही अच्छा होगा> = वास्तविक आकार (JDK7)
एलेक्स

यदि आप "आधुनिक जेवीएम इस मामले में चिंतनशील सरणी निर्माण का अनुकूलन करते हैं" के लिए एक प्रशस्ति पत्र दे सकते हैं, तो मैं खुशी से इस जवाब को बढ़ाऊंगा।
टॉम पैनिंग

मैं यहां सीख रहा हूं। यदि इसके बजाय मैं उपयोग करता हूं: MyClass[] arr = myList.stream().toArray(MyClass[]::new);क्या यह सिंक्रनाइज़ और समवर्ती संग्रह के साथ मदद या चोट पहुंचाएगा। और क्यों? कृप्या।
६:१० बजे

3

.Array जाँचता है कि पास किया गया सरणी सही आकार का है (जो कि आपकी सूची से तत्वों को फिट करने के लिए पर्याप्त बड़ा है) और यदि ऐसा है, तो इसका उपयोग करता है। नतीजतन, यदि सरणी का आकार इसे आवश्यकता से छोटा प्रदान करता है, तो एक नया सरणी रिफ्लेक्सली बनाया जाएगा।

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

यकीनन तेज़ संस्करण एक सही आकार के सरणी को पास करना है, लेकिन जब तक आप यह साबित नहीं कर सकते कि यह कोड एक प्रदर्शन अड़चन है, अन्यथा साबित होने तक रनटाइम प्रदर्शन के लिए पठनीयता पसंद करें।


2

पहला मामला अधिक कुशल है।

ऐसा इसलिए है क्योंकि दूसरे मामले में:

MyClass[] arr = myList.toArray(new MyClass[0]);

रनटाइम वास्तव में एक खाली सरणी (शून्य आकार के साथ) बनाता है और फिर ऐरे विधि के अंदर वास्तविक डेटा को फिट करने के लिए एक और सरणी बनाता है। यह निर्माण निम्नलिखित कोड (jdk1.5.0_10 से लिया गया) का उपयोग करके प्रतिबिंब का उपयोग किया जाता है:

public <T> T[] toArray(T[] a) {
    if (a.length < size)
        a = (T[])java.lang.reflect.Array.
    newInstance(a.getClass().getComponentType(), size);
System.arraycopy(elementData, 0, a, 0, size);
    if (a.length > size)
        a[size] = null;
    return a;
}

पहले फॉर्म का उपयोग करके, आप दूसरे ऐरे के निर्माण से बचते हैं और प्रतिबिंब कोड से भी बचते हैं।


.Array () प्रतिबिंब का उपयोग नहीं करता है। कम से कम जब तक आप "कास्टिंग" को प्रतिबिंब के लिए, वैसे भी गिनती नहीं करते; ;-)।
जॉर्जी

ऐरे (टी []) करता है। इसके लिए उपयुक्त प्रकार की एक सरणी बनाने की आवश्यकता है। आधुनिक जेवीएम गैर-चिंतनशील संस्करण के समान गति के बारे में होने के लिए इस तरह के प्रतिबिंब का अनुकूलन करते हैं।
टॉम हॉल्टिन -

मुझे लगता है कि यह प्रतिबिंब का उपयोग करता है। JDK 1.5.0_10 सुनिश्चित करने के लिए करता है और प्रतिबिंब ही एकमात्र तरीका है जो मुझे एक प्रकार की एक सरणी बनाने के लिए पता है जो आप संकलन समय पर नहीं जानते हैं।
पनियागोटिस कोरोस

फिर स्रोत कोड में से एक उसका उदाहरण है (ऊपर या मेरा) एक पुराना है। अफसोस की बात है कि मुझे खदान के लिए सही उप-संस्करण संख्या नहीं मिली, हालांकि।
जॉर्जी

1
जॉर्जी, आपका कोड JDK 1.6 से है और यदि आप Arrays.copyTo पद्धति का कार्यान्वयन देखते हैं, तो आप देखेंगे कि कार्यान्वयन प्रतिबिंब का उपयोग करता है।
पनियागोटिस कोरोस

-1

दूसरा एक छोटा सा पठनीय है, लेकिन इतना कम सुधार है कि यह इसके लायक नहीं है। पहली विधि तेज है, रनटाइम पर कोई नुकसान नहीं है, इसलिए यही मैं उपयोग करता हूं। लेकिन मैं इसे दूसरा तरीका लिखता हूं, क्योंकि यह टाइप करने के लिए तेज़ है। फिर मेरी आईडीई ने इसे एक चेतावनी के रूप में चिह्नित किया और इसे ठीक करने की पेशकश की। एकल कीस्ट्रोक के साथ, यह दूसरे प्रकार से कोड को पहले एक में परिवर्तित करता है।


-2

सही आकार के सरणी के साथ 'toArray' का उपयोग करना बेहतर प्रदर्शन करेगा क्योंकि विकल्प पहले शून्य आकार के सरणी और फिर सही आकार के सरणी का निर्माण करेगा। हालाँकि, जैसा कि आप कहते हैं कि अंतर नगण्य होने की संभावना है।

इसके अलावा, ध्यान दें कि जेवैक कंपाइलर कोई अनुकूलन नहीं करता है। इन दिनों सभी अनुकूलन JIT / HotSpot संकलक द्वारा रनटाइम पर किए जाते हैं। मुझे किसी भी JVM में 'toArray' के आसपास किसी भी अनुकूलन के बारे में पता नहीं है।

आपके प्रश्न का उत्तर, काफी हद तक शैली का विषय है, लेकिन स्थिरता के लिए आपके द्वारा पालन किए जाने वाले किसी भी कोडिंग मानकों का हिस्सा बनना चाहिए (चाहे दस्तावेज हो या अन्यथा)।


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

-5

पूर्णांक के लिए नमूना कोड:

Integer[] arr = myList.toArray(new integer[0]);
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.