2016-07-28 9 views
13

का उपयोग करके दो स्तर की संरचना पर इटरेट करना मेरे पास निम्नलिखित दो स्तर XML संरचना है। बक्से की एक सूची, प्रत्येक में दराज की एक सूची है।नेस्टेड इटरेटर्स

<Boxes> 
    <Box id="0"> 
     <Drawers> 
      <Drawer id="0"/> 
      <Drawer id="1"/> 
      ... 
     </Drawers> 
    </Box> 
    <Box id="1"> 
... 
    </Box> 
</Boxes> 

मैं StAX का उपयोग कर इसे पार्स करने कर रहा हूँ और दो Iterators के माध्यम से संरचना उजागर:

  1. BoxIterator implements Iterator<Box>, Iterable<Box>
  2. Box implements Iterable<Drawer>
  3. DrawerIterator implements Iterator<Drawer>

मैं तो निम्न कर सकते हैं:

BoxIterator boxList; 
for (Box box : boxList) { 
    for (Drawer drawer : box) { 
    drawer.getId() 
    } 
} 
उन Iterators के हुड मैं StAX उपयोग कर रहा हूँ और उन दोनों को एक ही अंतर्निहित XMLStreamReader एक्सेस कर रहे हैं के तहत

। अगर मैं BoxIterator.next() पर कॉल करता हूं तो यह परिणाम को प्रभावित करेगा जो बाद में कॉल पर DrawerIterator.next() पर वापस कर दिया जाएगा क्योंकि कर्सर अगले बॉक्स में स्थानांतरित हो जाएगा।

क्या यह Iterator का अनुबंध तोड़ता है? StAX का उपयोग कर दो स्तर की संरचना पर पुन: प्रयास करने का एक बेहतर तरीका है?

+1

आपका वर्णन 'Box.iterator' रिटर्न की तरह एक नया' DrawerIterator' लग रहा है और है कि यदि ऐसा है तो है अनुबंध टूट नहीं किया जाएगा, के बाद से 'ड्रॉवरइटरेटर' को मौजूदा बॉक्स के अंदर केवल तत्वों को वापस करना चाहिए। – Thomas

+0

@ थॉमस 'बॉक्स.इटरेटर() 'प्रत्येक कॉल पर वही' ड्रावर इटरेटर 'वापस कर देगा, क्योंकि वे सभी एक ही अंतर्निहित धारा तक पहुंचेंगे। इसका तात्पर्य यह है कि 'Box.iterator') को पिछले कॉल द्वारा लौटाया गया 'ड्रॉवर इटरेटर' भी जादुई रूप से उन्नत होगा। सभी एक ही कर्सर की स्थिति में अंतर्निहित धारा तक पहुंचेंगे। – Roland

+0

आह मैं देखता हूं। वह तब अनुबंध तोड़ देगा। क्या आपको हर कॉल पर एक ही उदाहरण वापस करने की ज़रूरत है? यदि आप हर बार एक नया उदाहरण वापस लेते हैं और अनुक्रमिक रूप से पुनरावृत्त करते हैं (यानी कोई यादृच्छिक पहुंच नहीं) इससे कोई फर्क नहीं पड़ता कि कर्सर की स्थिति उन्नत हो गई होगी या नहीं। एक बॉक्स पर पुनरावृत्त करने के बाद 'ड्रॉर्स उस बॉक्स पर आगे कॉल करें' 'ड्रावरइटरेटर के' हैनक्स्ट() 'को झूठी वापसी करनी चाहिए। – Thomas

उत्तर

5

क्या यह Iterator का अनुबंध तोड़ता है?

सं

जावा Iterator दो "अनुबंध" लगाता है। पहला अनुबंध जावा इंटरफ़ेस स्वयं है, जो 3 विधियों की घोषणा करता है: hasNext(), next(), और remove()। कोई भी वर्ग जो इस Iterator इंटरफ़ेस को लागू करता है, उन विधियों को परिभाषित करना होगा।

hasNext() [...] TRUE देता यात्रा अधिक तत्व हैं:

दूसरा अनुबंध Iterator के व्यवहार को परिभाषित करता है। [...] next() पुनरावृत्ति में अगला तत्व लौटाता है [और] NoSuchElementException फेंकता है यदि पुनरावृत्ति में कोई और तत्व नहीं है।

यह संपूर्ण अनुबंध है।

यह सच है कि यदि अंतर्निहित XMLStreamReader उन्नत है, तो यह आपके BoxIterator और/या DrawerIterator को गड़बड़ कर सकता है। वैकल्पिक रूप से, BoxIterator.next() और/या DrawerIterator.next() को गलत बिंदुओं पर कॉल करना पुनरावृत्ति को गड़बड़ कर सकता है। हालांकि, सही ढंग से का उपयोग किया गया है, जैसे ऊपर दिए गए आपके उदाहरण कोड में, यह कोड ठीक से काम करता है और कोड को बहुत सरल बनाता है। आपको केवल इटरेटर के उचित उपयोग को दस्तावेज करने की आवश्यकता है।

एक ठोस उदाहरण के रूप में, Scanner कक्षा Iterator<String> लागू करती है, और अभी तक कई अन्य विधियां हैं जो अंतर्निहित धारा को आगे बढ़ाती हैं। यदि Iterator वर्ग द्वारा लगाए गए एक मजबूत अनुबंध मौजूद थे, तो Scanner कक्षा स्वयं ही उल्लंघन कर रही है।


Ivan के रूप में अंक टिप्पणी में बाहर, boxList प्रकार class BoxIterator implements Iterator<Box>, Iterable<Box> का नहीं होना चाहिए। आप वास्तव में होना चाहिए:

class BoxList implements Iterable<Box> { ... } 
class BoxIterator implements Iterator<Box> { ... } 

BoxList boxList = ...; 
for (Box box : boxList) { 
    for (Drawer drawer : box) { 
    drawer.getId() 
    } 
} 

एक वर्ग को लागू दोनों Iterable और Iterator तकनीकी रूप से गलत आपके उपयोग के मामले के लिए नहीं है होने है, यह भ्रम की स्थिति पैदा कर सकते हैं।

एक और संदर्भ में इस कोड पर विचार करें:

List<Box> boxList = Arrays.asList(box1, box2, box3, box4); 
for(Box box : boxList) { 
    // Do something 
} 
for(Box box : boxList) { 
    // Do some more stuff 
} 

यहाँ, boxList.iterator(), दो बार कहा जाता है बक्से की सूची पुनरावृत्ति दो बार के लिए दो अलग-अलग Iterator<Box> उदाहरणों बनाने के लिए,। चूंकि boxList कई बार पुनरावृत्त किया जा सकता है, प्रत्येक पुनरावृत्ति के लिए एक नया इटरेटर उदाहरण की आवश्यकता होती है।

अपने कोड में:

BoxIterator boxList = new BoxIterator(xml_stream); 
for (Box box : boxList) { 
    for (Drawer drawer : box) { 
    drawer.getId(); 
    } 
} 

क्योंकि आप एक धारा से अधिक पुनरावृत्ति कर रहे हैं, आप नहीं (धारा रीवाइंड, या निकाले वस्तुओं के भंडारण के बिना) एक ही नोड पर दूसरी बार पुनरावृति कर सकते हैं। एक दूसरी कक्षा/वस्तु की आवश्यकता नहीं है; वही वस्तु इटरटेबल और इटरेटर दोनों के रूप में कार्य कर सकती है ... जो आपको एक वर्ग/वस्तु बचाती है।

यह कहकर, समयपूर्व अनुकूलन सभी बुराइयों की जड़ है। एक वर्ग/वस्तु की बचत संभावित भ्रम के लायक नहीं है; आपको BoxIterator को BoxList implements Iterable<Box>, और BoxIterator implements Iterator<Box> में विभाजित करना चाहिए।

+1

असल में, कोड नमूना इतना अच्छा नहीं है, क्योंकि BoxIterator क्लास Iterable और Iterator दोनों है। चीजें एक ही उदाहरण के दूसरे उपयोग पर गन्दा हो सकती हैं, अगर इटरेटर की स्थिति रीसेट नहीं होती है। –

+0

@IvanGammel आपके पास एक बिंदु है। BoxIterator बस 'it' को' iterator() 'पर कॉल में लौटाता है और अंतर्निहित XMLStreamReader पर कर्सर स्थिति रीसेट नहीं होती है। तो शायद मुझे पूरे इटरेटर, इटेरेबल प्रतिमान का उपयोग नहीं करना चाहिए। मैंने इसे केवल लूप के लिए बढ़ाया, यानी सिंटैक्टिक चीनी का उपयोग करने में सक्षम होने के लिए किया। – Roland

+2

@ रोलैंड, जबकि एक्सएमएल को पार्स करने का सामान्य तरीका नहीं है, इनपुट का विशाल होने पर आपका उपयोग केस वैध है और आपके पास छोटी ढेर सीमा है (अन्यथा आप एक्सएमएलबीन या एक्सस्ट्रीम के साथ मॉडल ऑब्जेक्ट करने के लिए पूरी फाइल को पार्स कर सकते हैं), ताकि आप कर सकें वास्तव में इस दृष्टिकोण का उपयोग करें (मेरे लिए एक सक्रिय रिकॉर्ड पैटर्न की तरह दिखता है)। आपको बस इसे सावधानीपूर्वक लागू करने की आवश्यकता है। –

3

यह संभावित कारण यह है कि hasNext()true लौट सकता है, लेकिन next() एक NoSuchElementException फेंक सकता है के लिए अनुबंध को तोड़ने के लिए है।

hasNext() का अनुबंध है:

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

लेकिन यह भी हो सकता है कि hasNext() और next() बुला के बीच, एक और इटरेटर ऐसी है कि धारा स्थिति में ले जाया जा सकता था वहाँ कोई और तत्व नहीं हैं।

हालांकि, जिस तरह से आपने इसका उपयोग किया है (नेस्टेड लूप), आपको ब्रेकेज का सामना नहीं करना पड़ेगा।

यदि आप किसी अन्य प्रक्रिया में इटरेटर पास करना चाहते हैं, तो आप इस ब्रेकेज का सामना कर सकते हैं।

+0

आपके द्वारा इंगित की गई समस्या किसी भी 'इटरेटर' के साथ हो सकती है, नहीं? यदि 'हैनक्स्ट()' को कॉल करने के बाद आप एक 'इटरेटर' को दूसरी प्रक्रिया में पास करते हैं जो इसे खपत करता है, तो 'अगला()' आपको अपेक्षित चीज़ों को वापस नहीं देगा। – Roland

+1

@ रोलैंड मेरा मतलब था कि * एक और * इटरेटर को दूसरी प्रक्रिया में देने से एक इटरेटर प्रभावित हो सकता है। कॉलिंग 'अगला() 'सभी * इटरेटर को प्रभावित करता है क्योंकि वे समान अंतर्निहित इनपुट साझा करते हैं। – Bohemian

+1

(लगभग) प्रत्येक पुनरावर्तक _something_ के साथ अंतर्निहित राज्य साझा करता है। और यहां तक ​​कि जब 'हैनक्स्ट() 'सत्य' लौटाता है, तब भी यह गारंटी नहीं देता है कि 'अगली()', जिसे तुरंत बाद में _ कहा जाता है, _always_ सफल होगा; यह एक 'ConcurrentModificationException' फेंक सकता है। Iterators सिर्फ मददगार हैं; वे अक्सर वाक्य रचनात्मक रूप से सुविधाजनक होते हैं, लेकिन वे कभी भी कुछ संरचनाओं पर "टूटा या भ्रष्ट नहीं किया जा सकता" की गारंटी नहीं देते हैं। – AJNeufeld

0

यह नहीं दिखता है जैसे कि यह आप ध्यान से कार्यान्वित कर रहे हैं/Iterator इंटरफेस को लागू करने से BoxIterator & DrawerIterator में next() & hasNext() तरीकों अधिभावी प्रदान की अनुबंध टूट जाएगा। कहने की जरूरत नहीं है, देखभाल करने की स्पष्ट स्थिति यह है कि hasNext()true वापस लौटना चाहिए यदि next() एक तत्व लौटा रहा है और false यदि next() अपवाद दे रहा है।

लेकिन तुम क्यों BoxIteratorIterable<Box>

BoxIterator implements Iterator<Box>, Iterable<Box> लागू Box के लिए Iterable इंटरफ़ेस से iterator() विधि अधिभावी हमेशा BoxIterator का एक उदाहरण वापस जाने के लिए जा रहा है के बाद से बना दिया है क्या मैं नहीं समझ सकता है। यदि आपके पास इसके पीछे कोई अन्य उद्देश्य नहीं है, तो BoxIterator में इस सुविधा को समाहित करने का कोई उद्देश्य नहीं है।

2

कोड के अपने टुकड़े के साथ एकमात्र डिज़ाइन समस्या यह है कि BoxIteratorIterator और Iterable दोनों लागू करता है। आम तौर पर, Iterable ऑब्जेक्ट प्रत्येक स्टेटस Iterator प्रत्येक बार iterator() विधि कहलाता है। इसके कारण, दो पुनरावृत्तियों के बीच कोई हस्तक्षेप नहीं होना चाहिए, लेकिन आपको आंतरिक लूप से बाहर निकलने के लिए सही ढंग से लागू करने के लिए एक राज्य वस्तु की आवश्यकता होगी (शायद, आपके पास पहले से ही है, लेकिन मुझे इसे स्पष्टता के लिए जिक्र करना चाहिए)।

  1. राज्य ऑब्जेक्ट पार्सर के लिए दो तरीकों से पॉपएवेंट और peekEvent के साथ प्रॉक्सी की तरह कार्य करेगा। चोटी इटेटरेटर अंतिम घटना की जांच करेंगे, लेकिन इसका उपभोग नहीं करेंगे। पॉप पर वे अंतिम घटना का उपभोग करेंगे।
  2. BoxIterable#iterator() स्टार्टएलेमेंट (बॉक्स) का उपभोग करेगा और इसके बाद पुनरावर्तक लौटाएगा।
  3. BoxIterator#hasNext() घटनाओं को देखेगा और स्टार्टएमेंट या एंडलेमेंट प्राप्त होने तक उन्हें पॉप करेगा। फिर स्टार्टएलेमेंट (बॉक्स) प्राप्त होने पर यह केवल तभी सही होगा।
  4. BoxIterator#next() बॉक्स ऑब्जेक्ट को प्रारंभ करने के लिए स्टार्ट एलीमेंट या एंडलेमेंट तक प्राप्त होने तक विशेषता ईवेंट देखेंगे।
  5. Box#iterator() स्टार्टएलेमेंट (ड्रायर्स) ईवेंट का उपभोग करेगा और फिर ड्रॉवरइटरेटर वापस करेगा।
  6. DrawerIterator#hasNext() स्टार्टएमेंट या एंडलेमेंट प्राप्त होने तक peek-and-pop होगा। फिर यह केवल तभी वापस आ जाएगा जब स्टार्टएलेमेंट (ड्रावर)
  7. DrawerIterator#next() एंडलेमेंट (ड्रावर) प्राप्त होने तक विशेषता ईवेंट का उपभोग करेगा।

आपका उपयोगकर्ता कोड लगभग असंशोधित रहेगा:

BoxIterable boxList; 
/* 
* boxList must be an BoxIterable, which on call to iterator() returns 
* new BoxIterator initialized with current state of STaX parser 
*/ 
for (Box box : boxList) { 
    /* 
    * on following line new iterator is created and initialized 
    * with current state of parser 
    */ 
    for (Drawer drawer : box) { 
    drawer.getId() 
    } 
} 
+0

_ सामान्य रूप से, इटेरेबल ऑब्जेक्ट प्रत्येक स्टेटस इटरेटर को हर बार इटरेटर() विधि कहलाता है ._ यह मामला यहां नहीं है। 'बॉक्सइटरेटर' में एक अंतर्निहित 'XMLStreamReader' है, इसलिए मैं केवल 'it' को' iterator() 'विधि में वापस कर देता हूं। इस इटरेटर की स्थिति वह स्थिति होगी जहां कर्सर अंतर्निहित धारा पर होता है। – Roland

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