Почему я получаю исключение UnsupportedOperationException при попытке удалить элемент из списка?

у меня есть этот код:

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)

Как это было бы правильно? Ява.15

12 ответов


очень много проблем с вашим кодом:

On Arrays.asList возврат списка фиксированного размера

из API:

Arrays.asList: возвращает фиксированного размера списка поддержанный указанным массива.

нельзя add к нему; вы не можете remove от него. Вы не можете структурно изменить List.

исправить

создать LinkedList, который поддерживает быстрее remove.

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

On split принимая regex

из API:

String.split(String regex): разбивает эту строку вокруг матчей данного регулярные выражения.

| является метасимволом regex; если вы хотите разделить на литерал |, вы должны избежать его \|, который как строковый литерал Java "\|".

исправления:

template.split("\|")

по лучшему алгоритму

вместо remove по одному со случайными индексами, лучше генерировать достаточное количество случайных чисел в диапазоне, а затем пересекать List С listIterator(), называя remove() при соответствующих индексах. Есть вопросы по stackoverflow о том, как генерировать случайные, но разные числа в заданном диапазоне.

этого, ваш алгоритм будет O(N).


этот сжег меня много раз. Arrays.asList создает неизменяемый список. Из Javadoc: возвращает фиксированного размера список, поддерживаемый указанным массивом.

создать новый список с тем же содержанием:

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

это создаст немного лишнего мусора, но вы сможете видоизменить ее.


вероятно, потому, что вы работаете с неизменяемым фантик.

измените эту строку:

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

на эту строку:

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

Я думаю, что заменить:

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

С

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

решает проблему.


просто прочитайте JavaDoc для метода asList:

возвращает {@code List} объектов в указанном массиве. Размер невозможно изменить список {@code, т. е. добавление и удаление неподдерживаемый, но элементы могут быть набор. Установка элемента изменяет основной массив.

это из Java 6, но похоже, что это то же самое для Android java.

редактировать

тип результирующий список Arrays.ArrayList, который является частным классом внутри массивов.класс. Практически говоря, это не что иное, как представление списка в массиве, который вы прошли с Arrays.asList. Как следствие: если вы измените массив, список также будет изменен. И поскольку массив нельзя изменить, удалите и добавьте операцию должны быть безосновным.


массивы.asList () возвращает список, который не позволяет операциям влиять на его размер (обратите внимание, что это не то же самое, что "unmodifiable").

вы могли бы сделать new ArrayList<String>(Arrays.asList(split)); чтобы создать реальную копию, но видя, что вы пытаетесь сделать, вот еще одно предложение (у вас O(n^2) алгоритм прямо под этим).

вы хотите удалить list.size() - count (назовем это k) случайные элементы из списка. Просто выберите как можно больше случайных элементов и поменять их до конца k позиции списка, затем удалите весь этот диапазон (например, используя subList () и clear () на этом). Это превратит его в тощего и злого (O(k) более точное).

обновление: как отмечено ниже, этот алгоритм имеет смысл только в том случае, если элементы неупорядочены, например, если список представляет собой мешок. Если, с другой стороны, в списке есть разумный порядок, этот алгоритм не позволит сохранить его алгоритм (polygenelubricants вместо бы.)

обновление 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--;
  }
}

список, возвращенный Arrays.asList() может быть неизменным. Не могли бы вы попробовать

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

у меня есть другое решение этой проблемы:

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

работы на newList ;)


Это исключение UnsupportedOperationException возникает, когда вы пытаетесь выполнить некоторую операцию над коллекцией, где это не разрешено, и в вашем случае, когда вы вызываете Arrays.asList Он не возвращает java.util.ArrayList. Он возвращает java.util.Arrays$ArrayList это неизменяемый список. Вы не можете добавить к нему и не можете удалить из него.


Да, о Arrays.asList, возвращает список фиксированного размера.

кроме использования связанного списка, просто используйте addAll список методов.

пример:

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

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

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

parentRecepeIdList.add("555");

Ниже приведен фрагмент кода из массивов

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;

Итак, что происходит, когда вызывается метод asList, он возвращает список своей собственной частной статической версии класса, которая не переопределяет add funcion из AbstractList для хранения элемента в массиве. Поэтому по умолчанию метод add в абстрактном списке вызывает исключение.

таким образом, это не обычный список массивов.


вы не можете удалить или добавить в список массивов фиксированного размера.

но вы можете создать свой подсписок из списка.

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, который не является фиксированным размером, как массивы.asList