java - जावा में सूची बनाम प्रकार ArrayList टाइप करें





interface decoupling (13)


(3) संग्रह myCollection = नया ArrayList ();

मैं आमतौर पर इसका उपयोग कर रहा हूँ। और केवल अगर मुझे सूची विधियों की आवश्यकता है, तो मैं सूची का उपयोग करूंगा। ArrayList के साथ ही। आप हमेशा अधिक "संकीर्ण" इंटरफ़ेस पर स्विच कर सकते हैं, लेकिन आप अधिक "विस्तृत" पर स्विच नहीं कर सकते हैं।

(1) List<?> myList = new ArrayList<?>();

(2) ArrayList<?> myList = new ArrayList<?>();

मैं समझता हूं कि (1) के साथ, सूची इंटरफ़ेस के कार्यान्वयन को बदला जा सकता है। ऐसा लगता है कि (1) आम तौर पर आवश्यकता के बावजूद किसी एप्लिकेशन में उपयोग किया जाता है (मैं हमेशा इसका उपयोग करता हूं)।

मैं सोच रहा हूं कि कोई भी (2) का उपयोग करता है?

साथ ही, कितनी बार (और मैं एक उदाहरण प्राप्त कर सकता हूं) क्या स्थिति वास्तव में (1) से अधिक (2) (यानी जहां (2) पर्याप्त नहीं होगी .. इंटरफेस और सर्वोत्तम प्रथाओं आदि के लिए कोडिंग के अलावा )




मैं सोच रहा हूं कि कोई भी (2) का उपयोग करता है?

हाँ। लेकिन शायद ही कभी एक अच्छे कारण के लिए।

और कभी-कभी लोग जलाते हैं क्योंकि उन्होंने ArrayList उपयोग किया था जब उन्हें इस्तेमाल किया जाना चाहिए List :

  • Collections.singletonList(...) Arrays.asList(...) या Arrays.asList(...) जैसे उपयोगिता विधियों को एक ArrayList वापस नहीं करते हैं।

  • List एपीआई के तरीके एक ही प्रकार की सूची वापस करने की गारंटी नहीं देते हैं।

उदाहरण के लिए, https://.com/a/1481123/139985 में पोस्टर को "स्लाइसिंग" के साथ समस्याएं थीं क्योंकि ArrayList.sublist(...) एक ArrayList वापस नहीं करता है ... और उसने अपना कोड डिज़ाइन किया था ArrayList उपयोग अपनी सभी सूची चर के प्रकार के रूप में करें। उन्होंने एक नए ArrayList में sublist की प्रतिलिपि बनाकर समस्या को हल करने "समाप्त" किया।

यह तर्क कि आपको यह जानने की आवश्यकता है कि List कैसे व्यवहार करती है उसे बड़े पैमाने पर RandomAccess मार्कर इंटरफ़ेस का उपयोग करके संबोधित किया जाता है। हां, यह थोड़ा गुंजाइश है, लेकिन विकल्प खराब है।

साथ ही, वास्तव में स्थिति (1) से अधिक (2) (यानी जहां (2) पर्याप्त नहीं होगा .. 'इंटरफेस को कोडिंग' और सर्वोत्तम प्रथाओं आदि के आधार पर वास्तव में कितनी बार आवश्यकता होती है)

प्रश्न का "कितनी बार" हिस्सा निष्पक्ष रूप से असंभव है।

(और क्या मैं एक उदाहरण प्राप्त कर सकता हूं)

कभी-कभी, एप्लिकेशन की आवश्यकता हो सकती है कि आप ArrayList API में विधियों का उपयोग करें जो List API में नहीं हैं। उदाहरण के लिए, ensureCapacity(int) , trimToSize() या removeRange(int, int) । (और आखिरी वाला केवल तभी उत्पन्न होगा जब आपने ऐरेलिस्ट का उप-प्रकार बनाया है जो public होने की विधि घोषित करता है।)

इंटरफ़ेस, आईएमओ की बजाय कक्षा में कोडिंग के लिए यही एकमात्र ध्वनि कारण है।

(यह सैद्धांतिक रूप से संभव है कि आपको प्रदर्शन में थोड़ा सुधार मिलेगा ... कुछ परिस्थितियों में ... कुछ प्लेटफार्मों पर ... लेकिन जब तक आपको वास्तव में उस अंतिम 0.05% की आवश्यकता नहीं है, यह करने योग्य नहीं है। यह एक नहीं है ध्वनि कारण, आईएमओ।)

यदि आप नहीं जानते कि यादृच्छिक पहुंच कुशल है या नहीं, तो आप कुशल कोड नहीं लिख सकते हैं।

यह एक वैध बिंदु है। हालांकि, जावा उस से निपटने के बेहतर तरीके प्रदान करता है; जैसे

public <T extends List & RandomAccess> void test(T list) {
    // do stuff
}

यदि आप उस सूची के साथ कॉल करते हैं जो RandomAccess लागू नहीं करता है तो आपको संकलन त्रुटि मिल जाएगी।

आप गतिशील रूप से परीक्षण भी कर सकते हैं ... instanceof का उपयोग कर ... यदि स्थैतिक टाइपिंग बहुत अजीब है। और आप अलग-अलग एल्गोरिदम (गतिशील रूप से) का उपयोग करने के लिए अपना कोड भी लिख सकते हैं, इस पर निर्भर करता है कि कोई सूची यादृच्छिक पहुंच समर्थित है या नहीं।

ध्यान दें कि ArrayList एकमात्र सूची वर्ग नहीं है जो RandomAccess लागू करता है। अन्य में CopyOnWriteList , Stack और Vector

मैंने देखा है कि लोग Serializable बारे में एक ही तर्क करते हैं (क्योंकि List इसे लागू नहीं करती है) ... लेकिन उपरोक्त दृष्टिकोण भी इस समस्या को हल करता है। (इस हद तक कि यह रनटाइम प्रकारों का उपयोग कर हल करने योग्य है । यदि कोई तत्व धारावाहिक नहीं है तो एक ArrayList serialization विफल हो जाएगा।)




एकमात्र मामला जिसे मैं जानता हूं कि (2) जीडब्ल्यूटी का उपयोग करते समय बेहतर हो सकता है, क्योंकि यह एप्लिकेशन पदचिह्न को कम करता है (मेरा विचार नहीं, लेकिन Google वेब टूलकिट टीम ऐसा कहता है)। लेकिन JVM (1) के अंदर चल रहे नियमित जावा के लिए शायद हमेशा बेहतर होता है।




उदाहरण के लिए आप तय कर सकते हैं कि LinkedList आपके आवेदन के लिए सबसे अच्छा विकल्प है, लेकिन बाद में निर्णय लेता है कि प्रदर्शन कारण के लिए ArrayList शायद बेहतर विकल्प हो सकता है।

उपयोग:

List list = new ArrayList(100); // will be better also to set the initial capacity of a collection 

के बजाय:

ArrayList list = new ArrayList();

सन्दर्भ के लिए:

(ज्यादातर संग्रह आरेख के लिए पोस्ट किया गया)







मैं (2) का उपयोग करता हूं यदि कोड सूची का "मालिक" है। यह उदाहरण के लिए स्थानीय-केवल चर के लिए सच है। ArrayList बजाय सार प्रकार List का उपयोग करने का कोई कारण नहीं है। स्वामित्व का प्रदर्शन करने के लिए एक और उदाहरण:

public class Test {

    // This object is the owner of strings, so use the concrete type.
    private final ArrayList<String> strings = new ArrayList<>();

    // This object uses the argument but doesn't own it, so use abstract type.
    public void addStrings(List<String> add) {
        strings.addAll(add);
    }

    // Here we return the list but we do not give ownership away, so use abstract type. This also allows to create optionally an unmodifiable list.
    public List<String> getStrings() {
        return Collections.unmodifiableList(strings);
    }

    // Here we create a new list and give ownership to the caller. Use concrete type.
    public ArrayList<String> getStringsCopy() {
        return new ArrayList<>(strings);
    }
}



निम्नलिखित दो में से:

(1) List<?> myList = new ArrayList<?>();
(2) ArrayList<?> myList = new ArrayList<?>();

सबसे पहले आम तौर पर पसंद किया जाता है। चूंकि आप केवल List इंटरफ़ेस से विधियों का उपयोग करेंगे, यह आपको भविष्य में LinkedList जैसे List कुछ अन्य कार्यान्वयन का उपयोग करने की स्वतंत्रता प्रदान करता है। तो यह आपको विशिष्ट कार्यान्वयन से decouples। अब उल्लेख करने के लायक दो अंक हैं:

  1. हमें हमेशा इंटरफेस के लिए प्रोग्राम करना चाहिए। here और अधिक
  2. आप ArrayList पर ArrayList का उपयोग करके लगभग हमेशा समाप्त हो जाएंगे। here और अधिक

मैं सोच रहा हूं कि कोई भी उपयोग करता है (2)

हां कभी-कभी (शायद ही कभी पढ़ा जाता है)। जब हमें उन तरीकों की आवश्यकता होती है जो ArrayList के कार्यान्वयन का हिस्सा हैं लेकिन इंटरफ़ेस List का हिस्सा नहीं हैं। उदाहरण के लिए ensureCapacity

साथ ही, कितनी बार (और मैं एक उदाहरण प्राप्त कर सकता हूं) क्या वास्तव में स्थिति (1) से अधिक (2) का उपयोग करने की आवश्यकता होती है

लगभग हमेशा आप विकल्प पसंद करते हैं (1)। यह ओओपी में शास्त्रीय डिज़ाइन पैटर्न है जहां आप हमेशा अपने कोड को विशिष्ट कार्यान्वयन और प्रोग्राम से इंटरफ़ेस में डीकुल करने का प्रयास करते हैं।




सूची एक इंटरफ़ेस है। इसमें विधियां नहीं हैं। जब आप एक सूची संदर्भ पर विधि कॉल करते हैं। यह वास्तव में दोनों मामलों में ArrayList की विधि को बुलाता है।

और भविष्य के लिए आप List obj = new ArrayList<> List obj = new LinkList<> या अन्य प्रकार को सूचीबद्ध कर सकते हैं जो सूची इंटरफ़ेस लागू करता है।




दरअसल ऐसे मौके होते हैं जहां (2) न केवल पसंदीदा बल्कि अनिवार्य है और मैं बहुत हैरान हूं, कि कोई भी इसका उल्लेख नहीं करता है।

क्रमबद्धता!

यदि आपके पास एक धारावाहिक वर्ग है और आप इसे एक सूची में रखना चाहते हैं, तो आपको फ़ील्ड को कंक्रीट और धारावाहिक प्रकार के रूप में घोषित करना होगा जैसे कि ArrayList क्योंकि List इंटरफ़ेस java.io.Serializable विस्तार नहीं करता है। Serializable

जाहिर है ज्यादातर लोगों को क्रमबद्ध करने की आवश्यकता नहीं है और इसके बारे में भूल जाओ।

एक उदाहरण:

public class ExampleData implements java.io.Serializable {

// The following also guarantees that strings is always an ArrayList.
private final ArrayList<String> strings = new ArrayList<>();



मैं कहूंगा कि 1 को प्राथमिकता दी जाती है

  • आप ArrayList में वैकल्पिक व्यवहार * के कार्यान्वयन के आधार पर हैं, उस मामले में स्पष्ट रूप से ArrayList का उपयोग करना अधिक स्पष्ट है
  • आप एक विधि कॉल में ArrayList का उपयोग करेंगे जिसके लिए संभवतः वैकल्पिक व्यवहार या प्रदर्शन विशेषताओं के लिए ArrayList की आवश्यकता होती है

मेरा अनुमान है कि 99% मामलों में आप सूची के साथ प्राप्त कर सकते हैं, जिसे प्राथमिकता दी जाती है।

  • उदाहरण के लिए add(null) , या add(null)



किसी ने इसे फिर से पूछा (डुप्लिकेट) जिसने मुझे इस मुद्दे पर थोड़ा गहरा कर दिया।

public static void main(String[] args) {
    List<String> list = new ArrayList<String>();
    list.add("a");
    list.add("b");

    ArrayList<String> aList = new ArrayList<String>();
    aList.add("a");
    aList.add("b");

}

अगर हम एक बाइटकोड दर्शक का उपयोग करते हैं (मैंने http://asm.ow2.org/eclipse/index.html उपयोग किया है) हम अपनी सूची स्निपेट के लिए निम्नलिखित (केवल सूची प्रारंभिकरण और असाइनमेंट) देखेंगे:

   L0
    LINENUMBER 9 L0
    NEW ArrayList
    DUP
    INVOKESPECIAL ArrayList.<init> () : void
    ASTORE 1
   L1
    LINENUMBER 10 L1
    ALOAD 1: list
    LDC "a"
    INVOKEINTERFACE List.add (Object) : boolean
    POP
   L2
    LINENUMBER 11 L2
    ALOAD 1: list
    LDC "b"
    INVOKEINTERFACE List.add (Object) : boolean
    POP

और अलगाव के लिए :

   L3
    LINENUMBER 13 L3
    NEW java/util/ArrayList
    DUP
    INVOKESPECIAL java/util/ArrayList.<init> ()V
    ASTORE 2
   L4
    LINENUMBER 14 L4
    ALOAD 2
    LDC "a"
    INVOKEVIRTUAL java/util/ArrayList.add (Ljava/lang/Object;)Z
    POP
   L5
    LINENUMBER 15 L5
    ALOAD 2
    LDC "b"
    INVOKEVIRTUAL java/util/ArrayList.add (Ljava/lang/Object;)Z
    POP

अंतर यह है कि सूची INVOKEINTERFACE को कॉल करने के समाप्त होती है जबकि एक सूची INVOKEVIRTUAL को कॉल करती है । बायकोड रूपरेखा प्लगइन संदर्भ में एन्कोडिंग,

invocinterface का उपयोग जावा इंटरफेस में घोषित एक विधि का आह्वान करने के लिए किया जाता है

जबकि invocvirtual

इंटरफ़ेस विधियों को छोड़कर सभी विधियों को आमंत्रित करता है (जो invokeinterface का उपयोग करते हैं), स्थैतिक विधियां (जो invokestatic का उपयोग करें), और कुछ विशेष मामलों को invokespecial द्वारा संचालित।

संक्षेप में, invokevterual pops invokeinterface के लिए स्टैक से ऑब्जेक्ट को बंद कर देता है

दुभाषिया ऑपरेटर स्टैक से 'n' आइटम पॉप करता है, जहां 'n' बाइटकोड से लिया गया 8-बिट हस्ताक्षरित पूर्णांक पैरामीटर होता है। इन वस्तुओं में से पहला ऑब्जेक्ट है, उस ऑब्जेक्ट का संदर्भ जिसका तरीका कहलाया जा रहा है।

अगर मैं इसे सही ढंग से समझता हूं, तो अंतर मूल रूप से ऑब्जेक्टफ्रू को कैसे प्राप्त करता है




लगभग हमेशा पहले को प्राथमिकता दी जाती है। पहले का लाभ यह है कि List का कार्यान्वयन शेष कोड को प्रभावित किए बिना, उदाहरण के लिए एक LinkedList बदल सकता है)। यह एक ArrayList साथ करना मुश्किल होगा, न केवल इसलिए कि आपको ArrayList को LinkedList हर जगह बदलना होगा, बल्कि इसलिए भी कि आपने ArrayList विशिष्ट विधियों का उपयोग किया होगा।

आप here List कार्यान्वयन के बारे में पढ़ सकते हैं। आप एक ArrayList शुरू कर ArrayList , लेकिन जल्द ही बाद में पता चलता है कि एक और कार्यान्वयन अधिक उपयुक्त है।




जैसा कि सभी ने कहा था यह ऐसा करेगा

 new ArrayList<>(Arrays.asList(array))

और सरणी बनाने का सामान्य नवीनतम तरीका अवलोकन योग्य है

अवलोकनयोग्य सूची : एक सूची जो श्रोताओं को होने पर परिवर्तनों को ट्रैक करने की अनुमति देती है।

जावा एसई के लिए आप कोशिश कर सकते हैं

FXCollections.observableArrayList(new Element(1), new Element(2), new Element(3));

यह ओरेकल डॉक्स के अनुसार है

अवलोकन योग्य ऐरेलिस्ट () एक नई खाली अवलोकन सूची बनाता है जिसे एक सरणीसूची द्वारा समर्थित किया जाता है। अवलोकन योग्य ऐरेलिस्ट (ई ... आइटम) इसमें जोड़े गए आइटमों के साथ एक नई अवलोकन योग्य सरणी सूची बनाता है।

जावा 9 अपडेट करें

जावा 9 में भी यह थोड़ा आसान है:

List<String> list = List.of("element 1", "element 2", "element 3");




java list interface decoupling