Bir öğeyi Listeden kaldırmaya çalışırken neden bir UnsupportedOperationException alıyorum?


476

Bu kodu var:

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, ", ");
}

Bunu anladım:

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)

Bu nasıl doğru bir yol olurdu? Java.15


LinkedList kullanın.
Lova Chittumuri

Yanıtlar:


1006

Kodunuzla ilgili birkaç sorun var:

On Arrays.asListsabit boyutlu bir liste dönen

API'dan:

Arrays.asList: Belirtilen dizi tarafından desteklenen sabit boyutlu bir liste döndürür .

Bunu yapamazsınız add; removeondan yapamazsın . Yapısal olarak değiştiremezsiniz List.

Fix

LinkedListDaha hızlı destekleyen bir oluşturun remove.

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

Açık splitregex alarak

API'dan:

String.split(String regex): Bu dizeyi verilen normal ifadenin eşleşmelerine böler .

|regex metakarakteridir; Değişmez bir bölünmüş istiyorsanız |, bunu kaçmak gerekir \|bir Java dize hangi olduğu gibi "\\|".

Düzeltme:

template.split("\\|")

Daha iyi algoritmada

removeRastgele indekslerle birer birer aramak yerine , aralıkta yeterli sayıda rasgele sayı üretmek ve daha sonra uygun endeksleri çağırarak Lista ile bir kerede geçmek daha iyidir . Belirli bir aralıkta rastgele ancak farklı sayıların nasıl oluşturulacağı konusunda yığın akışı hakkında sorular vardır.listIterator()remove()

Bununla algoritmanız olur O(N).


Teşekkürler, dize <10 sadece sınırlı öğeleri var, bu yüzden bir optimizasyon sorunu olmayacak.
Pentium10

6
@Pentium: bir şey daha: Randomher zaman yeni bir örnek oluşturmamalısınız . Bir statictarla yapın ve sadece bir kez tohumlayın.
poligenelubricants

6
LinkedList gerçekten daha hızlı mı? LinkedList ve ArrayList'in her ikisinde de O (n) kaldırılır: \ Bir ArrayList kullanmak neredeyse her zaman daha iyidir
gengkev

2
LinkedList vs ArrayList -> Ryan'ın bir performans testi grafiği var. LinkedList kaldırmada daha hızlıdır.
torno

LinkedList yalnızca kaldırılacak düğüm zaten biliniyorsa kaldırma işleminde çok daha hızlıdır . Bir öğeyi kaldırmaya çalışıyorsanız, liste her bir öğenin doğru olanı bulunana kadar karşılaştırılmasıyla değiştirilmelidir. Dizine göre kaldırmaya çalışıyorsanız, n geçiş gerçekleştirilmelidir. Bu geçişler süper pahalıdır ve CPU önbelleklemesi için en kötü durumdur: birçok bellekte öngörülemeyen şekillerde atlar. Bkz. Youtube.com/watch?v=YQs6IC-vgmo
Alexander - Monica'yı eski

143

Bu beni birçok kez yaktı. Arrays.asListdeğiştirilemez bir liste oluşturur. Javadoc'tan: Belirtilen dizi tarafından desteklenen sabit boyutlu bir liste döndürür .

Aynı içeriğe sahip yeni bir liste oluşturun:

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

Bu biraz ekstra çöp yaratacaktır, ancak mutasyona geçebileceksiniz.


6
Küçük bir nokta, ancak orijinal listeyi "kaydırmıyorsunuz", tamamen yeni bir liste oluşturuyorsunuz (bu yüzden işe yarıyor).
Jack Leow

Evet, JUnit test durumumda Arrays.asList () yöntemini kullandım ve haritamın içinde saklandım. İletilen listeyi kendi ArrayList'ime kopyalamak için kodumu değiştirdim.
cs94njw

Çözümünüz benim durumumda çalışmıyor, ancak açıklama için teşekkürler. Verdiğiniz bilgi benim çözümüme yol açtı.
Scott Biggs

54

Muhtemelen değiştirilemeyen ambalaj ile çalıştığınız için .

Bu satırı değiştirin:

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

bu satıra:

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

5
Arrays.asList () değiştirilemez bir paket değildir.
Dimitris Andreou

@polygenelubricants: Görünüşe göre karıştırıyorsun unmodifiableve immutable. unmodifiable"Değiştirilebilir, ancak yapısal olarak değil" anlamına gelir.
Roman

2
Sadece bir unmodifiableListsarıcı oluşturmayı denedim ve bir set; atar UnsupportedOperationException. Collections.unmodifiable*Sadece yapısal değil, tam değişmezlik anlamına geldiğinden oldukça eminim .
poligenelubricants

1
7 yıl sonra bu yorumları okurken kendime bu bağlantıyı göstermem için izin verdim: stackoverflow.com/questions/8892350/… değişmez ve değiştirilemez arasındaki farkı düzeltmek için burada tartıştım.
Nathan Ripert

14

Bence yerine:

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

ile

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

sorunu çözer.


5

Tarafından döndürülen liste Arrays.asList()değiştirilemez olabilir. Deneyebilir misin

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

1
o siler, ArrayList değerlerini silmek için en iyi veri yapısı değildir. LinkedList sorunu için çok daha fazla şey söylüyor.
Roma

2
LinkedList ile ilgili yanlış. Dizine erişiyor, bu yüzden LinkedList yineleme yoluyla bir öğe bulmak için çok zaman harcayacaktı. ArrayList kullanarak daha iyi bir yaklaşım için cevabımı görün.
Dimitris Andreou

4

Sadece asList yöntemi için JavaDoc'u okuyun:

Belirtilen dizideki nesnelerin bir {@code Listesi} döndürür. {@Code List} öğesinin boyutu değiştirilemez, yani ekleme ve kaldırma desteklenmez, ancak öğeler ayarlanabilir. Bir elemanın ayarlanması temel diziyi değiştirir.

Bu Java 6'dan ama android java için aynı gibi görünüyor.

DÜZENLE

Sonuç listesinin türü Arrays.ArrayList, Arrays.class içinde özel bir sınıftır. Pratik olarak, geçtiğiniz dizideki Liste görünümünden başka bir şey değildir Arrays.asList. Sonuç olarak: diziyi değiştirirseniz liste de değişir. Ve bir dizi yeniden boyutlandırılamadığından, kaldırma ve ekleme işleminin desteklenmemesi gerekir .


4

Arrays.asList (), boyutunu etkileyen işlemlere izin vermeyen bir liste döndürür (bunun "değiştirilemez" ile aynı olmadığını unutmayın).

new ArrayList<String>(Arrays.asList(split));Gerçek bir kopya oluşturmak için yapabilirsiniz , ancak ne yapmaya çalıştığınızı görmek, burada ek bir öneri ( O(n^2)hemen altında bir algoritmanız var).

Rastgele öğeleri listeden kaldırmak istiyorsunuz list.size() - count(buna diyelim k). Sadece rastgele sayıda öğe seçin ve bunları klistenin son konumlarına değiştirin , ardından tüm aralığı silin (örn. SubList () kullanarak ve bunun üzerinde clear ()). Bu onu yalın ve ortalama bir O(n)algoritmaya dönüştürür ( O(k)daha kesin).

Güncelleme : Aşağıda belirtildiği gibi, bu algoritma sadece elemanlar sıralanmamışsa, örneğin Liste bir Torbayı temsil ediyorsa mantıklıdır. Öte yandan, Liste anlamlı bir sıraya sahipse, bu algoritma onu korumaz (bunun yerine poligenel yağlayıcıların algoritması).

Güncelleme 2 : Geriye dönüp bakıldığında, daha iyi (doğrusal, sürdürme sırası, ancak O (n) rastgele sayılarla) algoritması şöyle bir şey olurdu:

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
Algoritma için +1, ancak OP sadece 10 öğe olduğunu söylüyor. Ve rastgele sayıları kullanmanın güzel bir yolu ArrayList. Benim önerimden çok daha basit. Yine de öğelerin yeniden sıralanmasıyla sonuçlanacağını düşünüyorum.
poligenelubricants

4

Bu sorun için başka bir çözümüm var:

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

üzerinde çalışmak newList;)


2

Bu UnsupportedOperationException koleksiyon üzerinde izin verilmeyen yerlerde bazı işlemler yapmaya çalıştığınızda ve sizin durumunuzda, çağırdığınızda Arrays.asListbir döndürmez java.util.ArrayList. java.util.Arrays$ArrayListDeğişmez bir liste olan bir döndürür . Ekleyemezsiniz ve kaldıramazsınız.


2

Evet, açık Arrays.asList, sabit boyutlu bir liste döndürüyor.

Bağlantılı bir liste kullanmak dışında, addAllyöntem listesini kullanmanız yeterlidir .

Misal:

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

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

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

parentRecepeIdList.add("555");

2

değiştirmek

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

için

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

veya

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

veya

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

veya (Öğeleri Kaldır için Daha İyi)

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

2

Arraylist narraylist = Arrays.asList (); // Değişmez arraylisti döndürür Değişken bir çözüm yapmak için: Arraylist narraylist = new ArrayList (Arrays.asList ());


1
SO hoş geldiniz. Cevabınız için teşekkür etmemize rağmen, diğer cevapların üzerine ek değer sağlaması daha iyi olur. Bu durumda, başka bir kullanıcı bu çözümü zaten gönderdiğinden cevabınız ek değer sağlamaz. Daha önceki bir yanıt size yardımcı olduysa, yeterli itibara sahip olduğunuzda oy vermelisiniz.
technogeek1995

1

Dizilerden kod snippet'i aşağıdadır

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;

yani asList yöntemi çağrıldığında kendi özel statik sınıf sürümünün listesini döndürür ve bu da diziyi elementte saklamak için AbstractList'ten add işlevini geçersiz kılmaz. Yani varsayılan olarak soyut listeye ekleme yöntemi istisna atar.

Yani normal dizi listesi değil.


1

Sabit boyutlu bir Diziler listesini kaldıramaz veya ekleyemezsiniz.

Ancak alt listenizi bu listeden oluşturabilirsiniz.

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, ", ");
}

* Diğer yol

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

bu Arrays.asList gibi sabit olmayan ArrayList oluşturur.


0

Arrays.asList() dahili olarak sabit boyutlu dizi kullanır.
Bunu dinamik olarak ekleyemez veya kaldıramazsınızArrays.asList()

Bunu kullan

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

İçinde narraylistkolayca öğe ekleyebilir veya kaldırabilirsiniz.


0

Yeni bir liste oluşturmak ve geçerli değerleri yeni listeye yerleştirmek benim için çalıştı.

Kod atma hatası -

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

Düzeltme işleminden sonra -

 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);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.