Perché ottengo un UnsupportedOperationException quando provo a rimuovere un elemento da un elenco?


476

Ho questo codice:

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

Capisco questo:

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)

Come sarebbe questo nel modo corretto? Java.15


usa LinkedList.
Lova Chittumuri,

Risposte:


1007

Alcuni problemi con il tuo codice:

Alla Arrays.asListrestituzione di un elenco di dimensioni fisse

Dall'API:

Arrays.asList: Restituisce un elenco di dimensioni fisse supportato dall'array specificato.

Non puoi addfarlo; non puoi removeda esso. Non è possibile modificare strutturalmente il file List.

fix

Crea un LinkedList, che supporta più velocemente remove.

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

Sul splitprendere regex

Dall'API:

String.split(String regex): Divide questa stringa attorno alle corrispondenze dell'espressione regolare fornita .

|è un regex metacharacter; se si desidera dividere un valore letterale |, è necessario evitarlo \|, come in una stringa Java letterale "\\|".

fix:

template.split("\\|")

Su un algoritmo migliore

Invece di chiamare removeuno alla volta con indici casuali, è meglio generare abbastanza numeri casuali nell'intervallo e quindi attraversare la Listvolta con un listIterator(), chiamando remove()a indici appropriati. Ci sono domande su StackOverflow su come generare numeri casuali ma distinti in un dato intervallo.

Con questo, il tuo algoritmo sarebbe O(N).


Grazie, ho solo elementi limitati nella stringa <10, quindi non sarà un problema di ottimizzazione.
Pentium10

6
@Pentium: un'altra cosa: non dovresti creare una nuova istanza di Randomogni volta. Trasformalo in un staticcampo e seminalo una sola volta.
poligenelubrificanti

6
LinkedList è davvero più veloce? Sia LinkedList che ArrayList hanno O (n) rimosso qui: \ È quasi sempre meglio usare semplicemente una ArrayList
gengkev

2
LinkedList vs ArrayList -> C'è un grafico di test delle prestazioni di Ryan. LinkedList è più veloce nella rimozione.
martedì

LinkedList è molto più veloce alla rimozione quando il nodo da rimuovere è già noto. Se stai cercando di rimuovere un elemento, l'elenco deve essere attraversato, con ogni elemento confrontato fino a quando non viene trovato quello corretto. Se si sta tentando di rimuovere per indice, è necessario eseguire n attraversamenti. Questi attraversamenti sono super costosi e il caso peggiore per la memorizzazione nella cache della CPU: molti salti nella memoria in modi imprevedibili. Vedi: youtube.com/watch?v=YQs6IC-vgmo
Alexander - Ripristina Monica l'

143

Questo mi ha bruciato molte volte. Arrays.asListcrea un elenco non modificabile. Da Javadoc: restituisce un elenco di dimensioni fisse supportato dall'array specificato.

Crea un nuovo elenco con lo stesso contenuto:

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

Questo creerà un po 'di immondizia in più, ma sarai in grado di mutarlo.


6
Punto minore, ma non stai "avvolgendo" l'elenco originale, stai creando un elenco completamente nuovo (motivo per cui funziona).
Jack Leow,

Sì, ho usato Arrays.asList () nel mio caso di test JUnit, che è stato poi archiviato nella mia mappa. Modificato il mio codice per copiare l'elenco passato nella mia ArrayList.
cs94njw,

La tua soluzione non funziona nella mia situazione, ma grazie per la spiegazione. Le conoscenze fornite mi hanno portato alla mia soluzione.
Scott Biggs,

54

Probabilmente perché stai lavorando con un wrapper non modificabile .

Cambia questa riga:

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

a questa linea:

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

5
Arrays.asList () non è un wrapper non modificabile.
Dimitris Andreou,

@polygenelubricants: sembra che tu abbia confuso unmodifiablee immutable. unmodifiablesignifica esattamente "modificabile, ma non strutturalmente".
Roman,

2
Ho appena provato a creare un unmodifiableListwrapper e provare a set; getta UnsupportedOperationException. Sono abbastanza sicuro Collections.unmodifiable*che significhi davvero immutabilità totale, non solo strutturale.
poligenelubrificanti

1
Leggendo questi commenti 7 anni dopo, mi permetto di indicare questo link: stackoverflow.com/questions/8892350/… probabilmente per correggere la differenza tra immutabile e non modificabile, discusso qui.
Nathan Ripert,

14

Penso che sostituendo:

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

con

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

risolve il problema.


5

L'elenco restituito da Arrays.asList()potrebbe essere immutabile. Potresti provare

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

1
sta eliminando, ArrayList non è la migliore struttura di dati per eliminare i suoi valori. LinkedList ha molti più problemi per il suo problema.
Roman,

2
Sbagliato riguardo alla LinkedList. Sta accedendo per indice, quindi LinkedList impiegherebbe tutto il tempo per trovare un elemento attraverso l'iterazione. Vedi la mia risposta per un approccio migliore, usando una ArrayList.
Dimitris Andreou,

4

Basta leggere JavaDoc per il metodo asList:

Restituisce un {@code List} degli oggetti nella matrice specificata. Le dimensioni di {@code List} non possono essere modificate, ovvero l'aggiunta e la rimozione non sono supportate, ma è possibile impostare gli elementi. L'impostazione di un elemento modifica l'array sottostante.

Questo è da Java 6 ma sembra che sia lo stesso per Android Java.

MODIFICARE

Il tipo dell'elenco risultante è Arrays.ArrayList, che è una classe privata all'interno di Arrays.class. In pratica, non è altro che una visualizzazione elenco sull'array con cui sei passato Arrays.asList. Di conseguenza: se si modifica l'array, anche l'elenco viene modificato. E poiché un array non è ridimensionabile, l'operazione di rimozione e aggiunta non deve essere supportata.


4

Arrays.asList () restituisce un elenco che non consente operazioni che incidono sulle sue dimensioni (si noti che questo non è lo stesso di "non modificabile").

Potresti fare new ArrayList<String>(Arrays.asList(split));per creare una copia reale, ma vedendo cosa stai cercando di fare, ecco un suggerimento aggiuntivo (hai un O(n^2)algoritmo proprio sotto quello).

Vuoi rimuovere list.size() - count(chiamiamolo così k) elementi casuali dalla lista. Basta scegliere quanti più elementi casuali e scambiarli alle kposizioni finali dell'elenco, quindi eliminare l'intero intervallo (ad esempio utilizzando subList () e clear () su quello). Ciò lo trasformerebbe in un O(n)algoritmo snello e medio ( O(k)è più preciso).

Aggiornamento : come indicato di seguito, questo algoritmo ha senso solo se gli elementi non sono ordinati, ad esempio se l'elenco rappresenta un sacchetto. Se, d'altra parte, l'Elenco ha un ordine significativo, questo algoritmo non lo preserverebbe (invece l'algoritmo dei poligenelubrificanti).

Aggiornamento 2 : Quindi in retrospettiva, un algoritmo migliore (lineare, di mantenimento, ma con numeri O (n) casuali) sarebbe qualcosa del genere:

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 per l'algoritmo, anche se OP dice che ci sono solo 10 elementi. E un bel modo di usare i numeri casuali con ArrayList. Molto più semplice del mio suggerimento. Penso che porterebbe a riordinare gli elementi però.
poligenelubrificanti

4

Ho un'altra soluzione per quel problema:

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

lavorare su newList;)


2

Questa UnsupportedOperationException viene quando si tenta di eseguire alcune operazioni sulla raccolta dove non è consentito e, nel caso in cui, Quando si chiama Arrays.asListnon viene restituito a java.util.ArrayList. Restituisce un java.util.Arrays$ArrayListche è un elenco immutabile. Non puoi aggiungerlo e non puoi rimuoverlo.


2

Sì, acceso Arrays.asList, restituendo un elenco di dimensioni fisse.

Oltre a utilizzare un elenco collegato, utilizzare semplicemente l' addAllelenco dei metodi.

Esempio:

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

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

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

parentRecepeIdList.add("555");

2

Sostituire

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

per

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

o

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

o

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

o (Meglio per rimuovere gli elementi)

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

2

Arraylist narraylist = Arrays.asList (); // Restituisce un arraylist immutabile Per renderlo la soluzione mutabile sarebbe: Arraylist narraylist = new ArrayList (Arrays.asList ());


1
Benvenuti in SO. Sebbene ti ringraziamo per la tua risposta, sarebbe meglio se fornisse un valore aggiuntivo rispetto alle altre risposte. In questo caso, la tua risposta non fornisce un valore aggiuntivo, poiché un altro utente ha già pubblicato quella soluzione. Se una risposta precedente ti è stata utile, dovresti votarla una volta che hai abbastanza reputazione.
technogeek1995,

1

Di seguito è riportato un frammento di codice proveniente da array

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;

quindi ciò che accade è che quando viene chiamato il metodo asList, viene restituito un elenco della propria versione di classe statica privata che non sostituisce la funzione di aggiunta da AbstractList per memorizzare l'elemento nell'array. Quindi, per impostazione predefinita, aggiungi il metodo nell'elenco astratto genera un'eccezione.

Quindi non è un normale elenco di array.


1

Non è possibile rimuovere, né è possibile aggiungere a un elenco di array a dimensione fissa.

Ma puoi creare la tua lista secondaria da quella lista.

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

* L'altro modo è

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

questo creerà ArrayList che non ha dimensioni fisse come Arrays.asList


0

Arrays.asList() utilizza un array di dimensioni fisse internamente.
Non puoi aggiungere o rimuovere dinamicamente da questoArrays.asList()

Usa questo

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

In narraylistpuoi facilmente aggiungere o rimuovere elementi.


0

La creazione di un nuovo elenco e il popolamento di valori validi nel nuovo elenco ha funzionato per me.

Errore di lancio del codice -

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

Dopo la correzione -

 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);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.