¿Por qué obtengo una excepción UnsupportedOperationException cuando intento eliminar un elemento de una Lista?


Tengo este código:

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

Entiendo esto:

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)

¿Cómo sería este el camino correcto? Java.15

Author: Pang, 2010-06-03

12 answers

Bastantes problemas con su código:

On Arrays.asList returning a fixed-size list

De la API:

Arrays.asList: Devuelve un fixed-size list respaldado por el array especificado.

No puedes add a ella; no puedes remove de ella. No se puede modificar estructuralmente el List.

Fix

Crear un LinkedList, que soporta más rápido remove.

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

En split tomando regex

De la API:

String.split(String regex): Divide esta cadena alrededor de las coincidencias de la expresión regular dada.

| es un metacarácter regex; si desea dividir en un literal |, debe escaparlo a \|, que como un literal de cadena de Java es "\\|".

Fix:

template.split("\\|")

En un mejor algoritmo

En lugar de llamar a remove uno a la vez con índices aleatorios, es mejor generar suficientes números aleatorios en el rango, y luego recorriendo el List una vez con un listIterator(), llamando a remove() en los índices apropiados. Hay preguntas en stackoverflow sobre cómo generar números aleatorios pero distintos en un rango dado.

Con esto, su algoritmo sería O(N).

 799
Author: polygenelubricants,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-06-03 12:19:15

Este me ha quemado muchas veces. Arrays.asList crea una lista no modificable. Desde Javadoc: Devuelve una lista de tamaño fijo respaldada por el array especificado.

Crea una nueva lista con el mismo contenido:

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

Esto creará un poco de basura extra, pero usted será capaz de mutar.

 110
Author: Nick Orton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-01 16:03:43

Probablemente porque estás trabajando con envoltura no modificable.

Cambie esta línea:

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

A esta línea:

List<String> list = new LinkedList<>(Arrays.asList(split));
 40
Author: Roman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-12 20:40:43

Creo que reemplazar:

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

Con

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

Resuelve el problema.

 9
Author: Salim Hamidi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-24 18:29:52

Simplemente lea el JavaDoc para el método asList:

Devuelve una {@lista de códigos} de los objetos en la matriz especificada. El tamaño de la lista de códigos {@} no se puede modificar, es decir, agregar y eliminar son sin soporte, pero los elementos pueden ser establecer. Establecer un elemento modifica el matriz subyacente.

Esto es de Java 6 pero parece que es lo mismo para Android java.

EDITAR

El tipo de la lista resultante es Arrays.ArrayList, que es un clase privada dentro de matrices.clase. Prácticamente hablando, no es más que una vista de lista en el array que has pasado con Arrays.asList. Con una consecuencia: si cambia la matriz, la lista también se cambia. Y debido a que un array no es redimensionable, remove and add operation debe no ser compatible.

 4
Author: Andreas_D,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-06-03 12:30:03

Matrices.asList () devuelve una lista que no permite operaciones que afecten su tamaño (tenga en cuenta que esto no es lo mismo que "no modificable").

Podrías hacer new ArrayList<String>(Arrays.asList(split)); para crear una copia real, pero viendo lo que estás tratando de hacer, aquí hay una sugerencia adicional (tienes un algoritmo O(n^2) justo debajo de eso).

Desea eliminar list.size() - count (llamemos a este k) elementos aleatorios de la lista. Simplemente elija tantos elementos aleatorios y cámbielos a las posiciones finales k de la lista, entonces eliminar todo ese rango (por ejemplo, usando subList () y clear () en eso). Eso lo convertiría en un algoritmo lean y mean O(n) (O(k) es más preciso).

Update: Como se indica a continuación, este algoritmo solo tiene sentido si los elementos no están ordenados, por ejemplo, si la Lista representa una Bolsa. Si, por otro lado, la Lista tiene un orden significativo, este algoritmo no preservarla (polygenelubricants' algoritmo contrario).

Actualización 2: Así que en retrospectiva, una mejor (lineal, manteniendo el orden, pero con O (n) números aleatorios) algoritmo sería algo como esto:

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--;
  }
}
 4
Author: Dimitris Andreou,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-06-03 13:03:53

La lista devuelta por Arrays.asList() podría ser inmutable. Podría intentar

List<String> list = new ArrayList(Arrays.asList(split));
 4
Author: Pierre,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-12 20:41:20

Tengo otra solución para ese problema:

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

Trabajo sobre newList ;)

 3
Author: ZZ 5,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-12 20:40:58

Esta excepción UnsupportedOperationException se produce cuando intenta realizar alguna operación en la colección donde no está permitido y, en su caso, Cuando llama a Arrays.asList no devuelve un java.util.ArrayList. Devuelve un java.util.Arrays$ArrayList que es una lista inmutable. No se puede añadir y no se puede quitar de ella.

 2
Author: Mayank Gupta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-06-03 13:30:40

Sí, en Arrays.asList, devolviendo una lista de tamaño fijo.

Aparte de usar una lista vinculada, simplemente use addAll lista de métodos.

Ejemplo:

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

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

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

parentRecepeIdList.add("555");
 2
Author: Sameer Kazi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-12 20:42:22

A continuación se muestra un fragmento de código de 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;

Entonces, lo que sucede es que cuando se llama al método asList, devuelve la lista de su propia versión de clase estática privada que no reemplaza la función add de AbstractList para almacenar el elemento en una matriz. Así que por defecto agregar método en lista abstracta lanza excepción.

Así que no es una lista de matrices regular.

 1
Author: Gagandeep Singh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-15 04:10:13

No se puede eliminar, ni se puede agregar a una lista de matrices de tamaño fijo.

Pero puedes crear tu sublista a partir de esa 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, ", ");
}

*Otra forma es

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

Esto creará ArrayList que no es de tamaño fijo como los Arrays.asList

 1
Author: Venkat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-12 20:42:09