सूची से किसी तत्व को निकालने का प्रयास करने पर मुझे UnsupportedOperationException क्यों मिलती है?


476

मेरे पास यह कोड है:

public static String SelectRandomFromTemplate(String template,int count) {
   String[] split = template.split("|");
   List<String> list=Arrays.asList(split);
   Random r = new Random();
   while( list.size() > count ) {
      list.remove(r.nextInt(list.size()));
   }
   return StringUtils.join(list, ", ");
}

मैंने इसे प्राप्त किया:

06-03 15:05:29.614: ERROR/AndroidRuntime(7737): java.lang.UnsupportedOperationException
06-03 15:05:29.614: ERROR/AndroidRuntime(7737):     at java.util.AbstractList.remove(AbstractList.java:645)

यह कैसे सही तरीका होगा? Java.15


लिंक्डलिस्ट का उपयोग करें।
लोवा चित्तमुरी

जवाबों:


1006

आपके कोड में कुछ समस्याएं हैं:

Arrays.asListनिश्चित आकार की सूची वापस करने पर

एपीआई से:

Arrays.asList: निर्दिष्ट सरणी द्वारा समर्थित एक निश्चित आकार की सूची देता है ।

आप इसे नहीं कर सकते add; आप इससे नहीं कर सकते remove। आप संरचनात्मक रूप से संशोधित नहीं कर सकते List

ठीक कर

एक बनाएँ LinkedList, जो तेजी से समर्थन करता है remove

List<String> list = new LinkedList<String>(Arrays.asList(split));

splitरेगेक्स लेने पर

एपीआई से:

String.split(String regex): इस स्ट्रिंग को दिए गए रेगुलर एक्सप्रेशन के मैचों के आसपास विभाजित करता है ।

|रेगेक्स मेटाचैकर है; यदि आप शाब्दिक रूप से विभाजित करना चाहते हैं |, तो आपको इसे बचाना चाहिए \|, जो कि जावा स्ट्रिंग शाब्दिक है "\\|"

ठीक कर:

template.split("\\|")

बेहतर एल्गोरिथ्म पर

removeयादृच्छिक सूचकांकों के साथ एक समय में एक कॉल करने के बजाय , रेंज में पर्याप्त यादृच्छिक संख्याओं को उत्पन्न करना बेहतर होता है, और फिर उपयुक्त सूचकांकों को कॉल करते हुए Listएक बार के साथ ट्रैवर्स करना । किसी श्रेणी में यादृच्छिक लेकिन अलग-अलग संख्याओं को उत्पन्न करने के तरीके पर स्टैकओवरफ़्लो पर सवाल हैं।listIterator()remove()

इसके साथ, आपका एल्गोरिथ्म होगा O(N)


धन्यवाद, मेरे पास स्ट्रिंग में केवल सीमित तत्व हैं <10 इसलिए अनुकूलन समस्या नहीं होगी।
पेंटियम

6
@Pentium: एक और बात: आपको Randomहर बार एक नया उदाहरण नहीं बनाना चाहिए । इसे एक staticक्षेत्र बनाएं और इसे केवल एक बार बीज दें।
पॉलीजेन लुब्रिकेंट

6
क्या लिंक्डलिस्ट वास्तव में तेज है? LinkedList और ArrayList दोनों के पास O (n) है यहां हटाएं: \ ArrayList का उपयोग करना लगभग हमेशा बेहतर होता है
gengkev

2
LinkedList बनाम ArrayList -> रयान से एक प्रदर्शन परीक्षण ग्राफ है। लिंक्डलिस्ट हटाने में तेज है।
बवंडर

लिंक्डलिस्ट केवल हटाने पर वास्तव में तेज़ होता है जब हटाए जाने वाले नोड को पहले से ही जाना जाता है। यदि आप किसी तत्व को निकालने का प्रयास कर रहे हैं, तो सूची को ट्रेस किया जाना चाहिए, क्योंकि प्रत्येक तत्व की तुलना तब तक की जाएगी जब तक कि सही एक नहीं मिल जाता है। यदि आप अनुक्रमणिका द्वारा निकालने का प्रयास कर रहे हैं, तो एन ट्रैवर्सल्स को किया जाना चाहिए। ये ट्रैवर्सल्स सुपर महंगे हैं, और सीपीयू कैशिंग के लिए सबसे खराब स्थिति है: अप्रत्याशित तरीके से मेमोरी के आसपास कई कूदता है। देखें: youtube.com/watch?v=YQs6IC-vgmo
अलेक्जेंडर - मोनिका को

143

इसने मुझे कई बार जलाया है। Arrays.asListएक अपरिवर्तनीय सूची बनाता है। Javadoc से: निर्दिष्ट सरणी द्वारा समर्थित एक निश्चित आकार की सूची देता है ।

समान सामग्री के साथ एक नई सूची बनाएँ:

newList.addAll(Arrays.asList(newArray));

यह थोड़ा अतिरिक्त कचरा पैदा करेगा, लेकिन आप इसे म्यूट कर पाएंगे।


6
मामूली बिंदु, लेकिन आप मूल सूची को "लपेटकर" नहीं कर रहे हैं, आप एक पूरी तरह से नई सूची बना रहे हैं (यही कारण है कि यह काम करता है)।
जैक लेओ

हाँ, मैंने अपने JUnit परीक्षण मामले में Arrays.asList () का उपयोग किया, जो तब मेरे नक्शे के अंदर संग्रहीत किया गया था। पास की गई सूची को कॉपी करने के लिए मेरे कोड को बदलकर, अपने खुद के ArrayList में बदल दिया।
cs94njw

आपका समाधान मेरी स्थिति में काम नहीं करता है, लेकिन स्पष्टीकरण के लिए धन्यवाद। आपके द्वारा प्रदान किया गया ज्ञान मेरे समाधान का कारण बना।
स्कॉट बिग्स

54

शायद इसलिए कि आप अनम्य आवरण के साथ काम कर रहे हैं ।

इस पंक्ति को बदलें:

List<String> list = Arrays.asList(split);

इस लाइन के लिए:

List<String> list = new LinkedList<>(Arrays.asList(split));

5
Arrays.asList () एक unmodifiable आवरण नहीं है।
दिमित्रीस आंद्रेउ

@ पॉलीऑक्सीज़ेलेब्रिकेंट: ऐसा लगता है कि आप मिश्रण करते हैं unmodifiableऔर immutableunmodifiableइसका मतलब बिल्कुल "परिवर्तनीय है, लेकिन संरचनात्मक रूप से नहीं"।
रोमन

2
मैंने सिर्फ एक unmodifiableListरैपर बनाने की कोशिश की और ए की कोशिश की set; यह फेंकता है UnsupportedOperationException। मैं काफी निश्चित रूप से Collections.unmodifiable*पूर्ण अपरिवर्तनीयता का मतलब है, न कि केवल संरचनात्मक।
पॉलीजेन लुब्रीकेंट

1
उन टिप्पणियों को पढ़ना 7 साल बाद मैं खुद को इस लिंक को इंगित करने की अनुमति देता हूं: stackoverflow.com/questions/8892350/… यहां चर्चा की गई अपरिवर्तनीय और अपरिवर्तनीय के बीच अंतर को ठीक करने की संभावना है।
नाथन रिपर्ट

14

मुझे लगता है कि इसकी जगह:

List<String> list = Arrays.asList(split);

साथ में

List<String> list = new ArrayList<String>(Arrays.asList(split));

समस्या का समाधान करता है।


5

द्वारा दी गई सूची Arrays.asList()अपरिवर्तनीय हो सकती है। क्या आप कोशिश कर सकते हैं?

List<String> list = new ArrayList(Arrays.asList(split));

1
वह हटा रहा है, ArrayList अपने मूल्यों को हटाने के लिए सबसे अच्छा डेटा संरचना नहीं है। लिंक्डलिस्ट अपनी समस्या के लिए बहुत अधिक संघर्ष करता है।
रोमन

2
लिंक्डलिस्ट के बारे में गलत। वह इंडेक्स द्वारा एक्सेस कर रहा है, इसलिए लिंक्डलिस्ट को इट्रेशन के माध्यम से एक तत्व खोजने में अधिक से अधिक समय लगेगा। एक बेहतर दृष्टिकोण के लिए मेरा जवाब देखें, एक ArrayList का उपयोग करते हुए।
दिमित्रिस आंद्रेउ

4

बस अस्लिस्ट विधि के लिए JavaDoc पढ़ें:

निर्दिष्ट सरणी में वस्तुओं का {@ कोड सूची} लौटाता है। {@Code List} के आकार को संशोधित नहीं किया जा सकता है, अर्थात जोड़ना और हटाना असमर्थित हैं, लेकिन तत्वों को सेट किया जा सकता है। एक तत्व सेट करना अंतर्निहित सरणी को संशोधित करता है।

यह जावा 6 से है, लेकिन ऐसा लगता है कि यह एंड्रॉइड जावा के लिए समान है।

संपादित करें

परिणामी सूची का प्रकार है Arrays.ArrayList, जो Arrays.class के अंदर एक निजी वर्ग है। व्यावहारिक रूप से, यह आपके द्वारा पास किए गए सरणी पर सूची-दृश्य के अलावा कुछ भी नहीं है Arrays.asList। एक परिणाम के साथ: यदि आप सरणी बदलते हैं, तो सूची भी बदल जाती है। और क्योंकि कोई सरणी रेज़िजेबल नहीं है, तो ऑपरेशन को निकालें और जोड़ें असमर्थित होना चाहिए।


4

Arrays.asList () एक सूची देता है जो अपने आकार को प्रभावित करने वाले कार्यों की अनुमति नहीं देता है (ध्यान दें कि यह "अनमॉडिफ़ेबल" के समान नहीं है)।

आप new ArrayList<String>(Arrays.asList(split));एक वास्तविक प्रतिलिपि बनाने के लिए कर सकते हैं , लेकिन यह देखते हुए कि आप क्या करने की कोशिश कर रहे हैं, यहां एक अतिरिक्त सुझाव है (आपके पास इसके O(n^2)ठीक नीचे एक एल्गोरिथ्म है)।

आप सूची से यादृच्छिक तत्वों को list.size() - count(इसे कॉल करने देना चाहते हैं) हटाना चाहते हैं k। बस कई यादृच्छिक तत्वों को चुनें और उन्हें kसूची के अंतिम स्थानों पर स्वैप करें , फिर उस पूरी सीमा को हटा दें (जैसे कि उप-सूची का उपयोग करके) और (उस पर स्पष्ट)। यह एक दुबला और मतलब O(n)एल्गोरिथ्म के लिए बदल जाएगा ( O(k)अधिक सटीक है)।

अद्यतन : जैसा कि नीचे उल्लेख किया गया है, यह एल्गोरिथ्म केवल तभी समझ में आता है जब तत्व अनियंत्रित होते हैं, जैसे कि सूची एक बैग का प्रतिनिधित्व करती है। यदि, दूसरी ओर, सूची में एक सार्थक आदेश है, तो यह एल्गोरिथ्म इसे संरक्षित नहीं करेगा (पॉलीजेनबेलिक लूब्रिकेंट्स एल्गोरिथ्म के बजाय)।

अद्यतन 2 : तो पूर्वव्यापी में, एक बेहतर (रैखिक, क्रम बनाए रखना, लेकिन O (n) यादृच्छिक संख्या के साथ) एल्गोरिथ्म कुछ इस तरह होगा:

LinkedList<String> elements = ...; //to avoid the slow ArrayList.remove()
int k = elements.size() - count; //elements to select/delete
int remaining = elements.size(); //elements remaining to be iterated
for (Iterator i = elements.iterator(); k > 0 && i.hasNext(); remaining--) {
  i.next();
  if (random.nextInt(remaining) < k) {
     //or (random.nextDouble() < (double)k/remaining)
     i.remove();
     k--;
  }
}

1
एल्गोरिथ्म के लिए +1, हालांकि ओपी कहता है कि केवल 10 तत्व हैं। और साथ यादृच्छिक संख्याओं का उपयोग करने का अच्छा तरीका है ArrayList। मेरे सुझाव से बहुत सरल। मुझे लगता है कि यह तत्वों के फिर से व्यवस्थित होने के परिणामस्वरूप होगा।
पॉलीजेन लुब्रिकेंट्स

4

मुझे उस समस्या का एक और समाधान मिल गया है:

List<String> list = Arrays.asList(split);
List<String> newList = new ArrayList<>(list);

काम पर newList;)


2

यह UnsupportedOperationException तब आती है जब आप संग्रह पर कुछ ऑपरेशन करने की कोशिश करते हैं जहां इसकी अनुमति नहीं है और आपके मामले में, जब आप कॉल Arrays.asListकरते हैं तो यह वापस नहीं आता है java.util.ArrayList। यह java.util.Arrays$ArrayListएक अपरिवर्तनीय सूची है। आप इसे नहीं जोड़ सकते हैं और आप इसे नहीं निकाल सकते।


2

हां, Arrays.asListएक निश्चित आकार की सूची लौटाते हुए।

लिंक की गई सूची का उपयोग addAllकरने के अलावा , बस विधि सूची का उपयोग करें ।

उदाहरण:

String idList = "123,222,333,444";

List<String> parentRecepeIdList = new ArrayList<String>();

parentRecepeIdList.addAll(Arrays.asList(idList.split(","))); 

parentRecepeIdList.add("555");

2

बदलने के

List<String> list=Arrays.asList(split);

सेवा

List<String> list = New ArrayList<>();
list.addAll(Arrays.asList(split));

या

List<String> list = new ArrayList<>(Arrays.asList(split));

या

List<String> list = new ArrayList<String>(Arrays.asList(split));

या (निकालें तत्वों के लिए बेहतर)

List<String> list = new LinkedList<>(Arrays.asList(split));

2

Arraylist narraylist = Arrays.asList (); // अपरिवर्तनीय सरणी सूची लौटाता है इसे परिवर्तनशील समाधान बनाना होगा: Arraylist narraylist = new ArrayList (Arrays.asList ());


1
एसओ में आपका स्वागत है। यद्यपि हम आपके उत्तर के लिए धन्यवाद देते हैं, लेकिन बेहतर होगा यदि वह अन्य उत्तरों के शीर्ष पर अतिरिक्त मूल्य प्रदान करे। इस मामले में, आपका उत्तर अतिरिक्त मूल्य प्रदान नहीं करता है, क्योंकि कोई अन्य उपयोगकर्ता पहले से ही उस समाधान को पोस्ट करता है। यदि पिछला उत्तर आपके लिए मददगार था, तो पर्याप्त प्रतिष्ठा होने के बाद आपको इसे वोट करना चाहिए।
technogeek1995

1

निम्नलिखित Arrays से कोड का स्निपेट है

public static <T> List<T> asList(T... a) {
        return new ArrayList<>(a);
    }

    /**
     * @serial include
     */
    private static class ArrayList<E> extends AbstractList<E>
        implements RandomAccess, java.io.Serializable
    {
        private static final long serialVersionUID = -2764017481108945198L;
        private final E[] a;

तो क्या होता है कि जब असिस्ट विधि को बुलाया जाता है तो यह अपने स्वयं के निजी स्थिर वर्ग संस्करण की सूची देता है जो एरेस्ट में तत्व को संग्रहित करने के लिए एब्सट्रेलिस्ट से फंकियन को नहीं जोड़ता है। तो अमूर्त सूची में डिफ़ॉल्ट रूप से विधि जोड़ अपवाद फेंकता है।

तो यह नियमित सरणी सूची नहीं है।


1

आप न तो हटा सकते हैं, न ही एरे के निश्चित-आकार की सूची में जोड़ सकते हैं।

लेकिन आप अपनी सूची उस सूची से बना सकते हैं।

list = list.subList(0, list.size() - (list.size() - count));

public static String SelectRandomFromTemplate(String template, int count) {
   String[] split = template.split("\\|");
   List<String> list = Arrays.asList(split);
   Random r = new Random();
   while( list.size() > count ) {
      list = list.subList(0, list.size() - (list.size() - count));
   }
   return StringUtils.join(list, ", ");
}

* अन्य तरीका है

ArrayList<String> al = new ArrayList<String>(Arrays.asList(template));

यह ArrayList बनाएगा जो Arrays.asList की तरह निश्चित आकार नहीं है


0

Arrays.asList() आंतरिक रूप से निश्चित आकार के सरणी का उपयोग करता है।
आप गतिशील रूप से इसे जोड़ या हटा नहीं सकतेArrays.asList()

इसे इस्तेमाल करो

Arraylist<String> narraylist=new ArrayList(Arrays.asList());

में narraylistआप आसानी से आइटम जोड़ या हटा सकते हैं।


0

एक नई सूची बनाना और नई सूची में मान्य मूल्यों को आबाद करना मेरे लिए काम किया।

कोड फेंकने की त्रुटि -

List<String> list = new ArrayList<>();
   for (String s: list) {
     if(s is null or blank) {
        list.remove(s);
     }
   }
desiredObject.setValue(list);

फिक्स के बाद -

 List<String> list = new ArrayList<>();
 List<String> newList= new ArrayList<>();
 for (String s: list) {
   if(s is null or blank) {
      continue;
   }
   newList.add(s);
 }
 desiredObject.setValue(newList);
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.