2012-06-04 15 views
11

EnumerationConcurrentModificationException फेंक नहीं देता है, क्यों?जावा गणना बनाम इटरेटर

कोड नीचे देखें।

public static void main(String[] args) { 

    Vector<String> v=new Vector<String>(); 
    v.add("Amit"); 
    v.add("Raj"); 
    v.add("Pathak"); 
    v.add("Sumit"); 
    v.add("Aron"); 
    v.add("Trek"); 

    Enumeration<String> en=v.elements(); 

    while(en.hasMoreElements()) 
    { 
     String value=(String) en.nextElement(); 
     System.out.println(value); 
     v.remove(value); 

    } 

} 

यह केवल प्रिंट:

 
Amit 
Pathak 
Aron 

इस तरह के व्यवहार क्यों है। क्या हम कह सकते हैं कि Enumerator धागा सुरक्षित है।

संपादित करें: इटरेटर के साथ काम करते समय यह एकल थ्रेड एप्लिकेशन में ConcurrentModificationException फेंकता है।

public static void main(String[] args) { 

    Vector<String> v=new Vector<String>(); 
    v.add("Amit"); 
    v.add("Raj"); 
    v.add("Pathak"); 
    v.add("Sumit"); 
    v.add("Aron"); 
    v.add("Trek"); 

    Iterator<String> it=v.iterator(); 
    while(it.hasNext()) 
    { 
     String value=(String) it.next(); 
     System.out.println(value); 
     v.remove(value); 
    } 
} 

कृपया जांचें।

+1

कृपया http://stackoverflow.com/questions/948194/different-using-java-util-enumeration-and-iterator – Garbage

+0

इस सवाल की पृष्ठभूमि क्या है पढ़ा है? क्या आप एक बहु थ्रेडेड वातावरण में इटरेटर्स की बजाय गणनाओं का उपयोग करने पर विचार कर रहे हैं? –

+0

क्या कहीं और कोड है जिसे हमें पता होना चाहिए? उपरोक्त नमूना कोड में कोई मल्टीथ्रेडिंग नहीं है, इसलिए जब तक आपके पास थ्रेड-सुरक्षा को दोष देने का कोई विशिष्ट कारण नहीं है, मुझे लगता है कि आप – posdef

उत्तर

4

गणन पढ़ सकते हैं ConcurrentModificationException फेंक नहीं है, क्यों?

क्योंकि इस अपवाद को फेंकने वाले कोड में कोई रास्ता नहीं है। संपादित करें: मैं वेक्टर वर्ग द्वारा प्रदान किए गए कार्यान्वयन का जिक्र कर रहा हूं, सामान्य रूप से गणना इंटरफ़ेस के बारे में नहीं।

ऐसा ऐसा व्यवहार क्यों है। क्या हम कह सकते हैं कि एन्युमरेटर थ्रेड सुरक्षित है।

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

आपके आउटपुट का कारण यह है कि गणना वस्तु एक काउंटर को बनाए रखती है, जो nextElement() के प्रत्येक आमंत्रण के बाद बढ़ी है। यह काउंटर remove() के आपके आमंत्रण से अवगत नहीं है।

+0

मेरी समझ है कि गणना में इटरेटर के रूप में संग्रह का संदर्भ है। जैसे ही हम वेक्टर से तत्व हटाते हैं, यह गणना में देखा जाता है। फिर यह प्रिंटिंग 3 क्यों नहीं है अगर हम पहले प्रिंट कर रहे हैं और फिर हटा रहे हैं। – amicngh

+0

यही बात है। गणना आपके निकालने का आविष्कार नहीं करती है (जो इसे इंडेक्स वैरिएबल में कमी करने के लिए necesarry बना देगा)। – MartinK

-1

v.remove(value) को हटा दें और सब कुछ उम्मीद के रूप में

काम करेंगे संपादित करें: क्षमा करें,

सवाल यह हालांकि threadsafety साथ कोई संबंध नहीं है पढ़ने में भूलना। आप मल्टीथ्रेडिंग भी नहीं कर रहे हैं इसलिए ऐसा कोई कारण नहीं है कि जावा इसके लिए अपवाद फेंक देगा।

आप अपवाद चाहते हैं जब आप बनाने के वेक्टर बदल रहे हैं यह Unmodifiable

+4

यह –

+0

से पूछे जाने वाले प्रश्न का उत्तर नहीं लगता है। –

10

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

ConcurrentHashMap एक विशेष मामला है, क्योंकि इसे स्पष्ट रूप से थ्रेड-सुरक्षित और संपादन योग्य होने के रूप में परिभाषित किया गया है (जो मुझे लगता है कि सभी थ्रेड-सुरक्षित संग्रहों के लिए सच है)।

वैसे भी, जब तक आप संग्रह को पुन: सक्रिय करने और संशोधित करने के लिए एक ही थ्रेड का उपयोग कर रहे हैं, तो ConcurrentHashMap आपकी समस्या का गलत समाधान है। आप एपीआई का गलत इस्तेमाल कर रहे हैं। वस्तुओं को हटाने के लिए आपको Iterator.remove() का उपयोग करना चाहिए। वैकल्पिक रूप से, आप मूल को पुन: सक्रिय और संशोधित करने से पहले संग्रह की एक प्रति बना सकते हैं।

संपादित करें:

मैं किसी भी गणन कि एक ConcurrentModificationException फेंकता का पता नहीं है। हालांकि, एक समवर्ती संशोधन के मामले में व्यवहार ऐसा नहीं हो सकता है जो आप उम्मीद करते हैं। जैसा कि आप उदाहरण में देखते हैं, गणना सूची में हर दूसरे तत्व को छोड़ देती है। यह हटाए जाने के बावजूद आंतरिक सूचकांक में वृद्धि के कारण है। तो यह क्या होता है:

  • en.nextElement() - वेक्टर से पहले तत्व देता है, वृद्धि कर देता है 1
  • v.remove (मान) के सूचकांक - वेक्टर से पहले तत्व निकाल देता है, बदलाव सभी तत्वों
  • छोड़ दिया
  • en.nextElement() - वेक्टर, जो अब "पाठक"

इटरेटर का असफल फास्ट व्यवहार बातें इस तरह का है, जिसके कारण यह आम तौर पर Enumberation बेहतर है से बचाता है से दूसरा तत्व देता है।

Iterator<String> it=v.iterator(); 
while(it.hasNext()) 
{ 
    String value=(String) it.next(); 
    System.out.println(value); 
    it.remove(); // not v.remove(value); !! 
} 

वैकल्पिक रूप से: इसके बजाय, आप निम्न करना चाहिए

for(String value : new Vector<String>(v)) // make a copy 
{ 
    String value=(String) it.next(); 
    System.out.println(value); 
    v.remove(value); 
} 

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

+0

अंत में मैं समझता हूं कि गणना कभी भी ConcurrentModificationException फेंक नहीं है? – amicngh

+1

+1 "ConcurrentModificationException के लिए multithreading या थ्रेड सुरक्षा के अर्थ में सहमति के साथ कुछ भी नहीं है" –

3

समवर्ती संशोधन में धागे से कोई लेना देना नहीं है।

यहां सहमति का अर्थ यह है कि आप संग्रह को संशोधित कर रहे हैं जबकि आप इसे फिर से सक्रिय कर रहे हैं। (आपके उदाहरण में यह एक ही थ्रेड में होता है।)

संग्रह के Iterators और गणना इस मामले में ConcurrentModificationException फेंक सकते हैं, लेकिन ऐसा नहीं है। जो लोग प्रदर्शित करते हैं, असफल तेज़ व्यवहार। जाहिर है, Vector की गणना विफल नहीं है।

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

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

कुछ इटरेटर इटरेटर के माध्यम से तत्वों को जोड़ने/स्थापित/हटाने की अनुमति देते हैं। यदि आपको वास्तव में समवर्ती संशोधन की आवश्यकता है तो यह एक अच्छा विकल्प हो सकता है।

1

संक्षिप्त उत्तर: यह bonus feature है जिसका अनुमान लगाया गया था कि गणना पहले से ही थी, इसलिए तथ्य यह है कि गणनाकर्ता इसे फेंक नहीं देता है, यह विशेष रूप से कुछ भी सुझाव नहीं देता है।

लांग जवाब:
विकिपीडिया से:

संग्रह कार्यान्वयन पूर्व JDK 1.2 में [...] एक संग्रह ढांचे को शामिल नहीं किया था। जावा ऑब्जेक्ट्स को समूहबद्ध करने के लिए मानक विधियां सरणी, वेक्टर, और हैशटेबल कक्षाओं, के माध्यम से दुर्भाग्य से विस्तारित नहीं थीं, और मानक सदस्य इंटरफ़ेस को लागू नहीं किया गया था। पुन: प्रयोज्य संग्रह डेटा संरचनाओं की आवश्यकता को हल करने के लिए [...] संग्रह ढांचे मुख्य रूप से जोशुआ ब्लोच द्वारा डिजाइन और विकसित किया गया था, और जेडीके 1.2 में पेश किया गया था।

जब बलोच टीम ने ऐसा ही किया, उन्होंने सोचा कि यह एक अच्छा विचार के लिए एक नया तंत्र है जो बाहर एक अलार्म (ConcurrentModificationException) जब अपने संग्रह एक मल्टी-थ्रेडेड कार्यक्रम में ठीक से सिंक्रनाइज़ नहीं कर रहे थे भेजता में डालने के लिए किया गया था। इस तंत्र के बारे में ध्यान देने के लिए दो महत्वपूर्ण बातें हैं: 1) यह समवर्ती बग पकड़ने की गारंटी नहीं है - अपवाद केवल तभी फेंक दिया जाता है जब आप भाग्यशाली हों। 2) अपवाद भी फेंक दिया जाता है यदि आप एक धागे का उपयोग करके संग्रह का दुरुपयोग करते हैं (जैसा कि आपके उदाहरण में है)।

तो, एक संग्रह कई धागे द्वारा उपयोग किए जाने पर ConcurrentModificationException फेंकने वाला नहीं है, यह किसी भी थ्रेड-सुरक्षित को इंगित नहीं करता है।

0

यह इस बात पर निर्भर करता है कि आप गणना कैसे प्राप्त करते हैं। निम्न उदाहरण देखें यह ConcurrentModificationException फेंक करता है:

import java.util.*; 

public class ConcurrencyTest { 
    public static void main(String[] args) { 

     Vector<String> v=new Vector<String>(); 
     v.add("Amit"); 
     v.add("Raj"); 
     v.add("Pathak"); 
     v.add("Sumit"); 
     v.add("Aron"); 
     v.add("Trek"); 

     Enumeration<String> en=Collections.enumeration(v);//v.elements(); 

     while(en.hasMoreElements()) 
     { 
      String value=(String) en.nextElement(); 
      System.out.println(value); 
      v.remove(value); 
     }    

     System.out.println("************************************"); 

     Iterator<String> iter = v.iterator(); 
      while(iter.hasNext()){ 
       System.out.println(iter.next()); 
       iter.remove(); 
       System.out.println(v.size()); 
     } 
    } 
} 

गणन सिर्फ एक इंटरफेस है, यह वास्तविक व्यवहार कार्यान्वयन पर निर्भर है। Collections.enumeration() कॉल से गणना कुछ समय में इटेटरेटर को लपेटती है, इसलिए यह वास्तव में असफल है, लेकिन Vector.elements() को कॉल करने से प्राप्त गणना नहीं है।

असफल-तेज़ गणना भविष्य में में अनिश्चित समय पर मनमाने ढंग से, गैर-निर्धारक व्यवहार पेश कर सकती है। उदाहरण के लिए: यदि आप इस तरह की मुख्य विधि लिखते हैं, तो यह पहले पुनरावृत्ति के बाद java.util.No.SuchElementException फेंक देगा।

public static void main(String[] args) { 

     Vector<String> v=new Vector<String>(); 
     v.add("Amit"); 
     v.add("Raj"); 
     v.add("Pathak"); 

     Enumeration<String> en = v.elements(); //Collections.enumeration(v); 

     while(en.hasMoreElements()) 
     { 
      v.remove(0); 
      String value=(String) en.nextElement(); 
      System.out.println(value); 
     }  
    } 
संबंधित मुद्दे