Почему я получаю исключение UnsupportedOperationException при попытке удалить элемент из списка? - PullRequest
418 голосов
/ 03 июня 2010

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

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

Ответы [ 15 ]

897 голосов
/ 03 июня 2010

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

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

Из API:

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

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

Fix

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

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

Вкл. split Принимая регулярное выражение

Из API:

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

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

Fix:

template.split("\\|")

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

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

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

124 голосов
/ 03 июня 2010

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

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

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

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

47 голосов
/ 03 июня 2010

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

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

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

к этой строке:

List<String> list = new LinkedList<>(Arrays.asList(split));
12 голосов
/ 09 сентября 2013

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

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

с

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

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

4 голосов
/ 03 июня 2010

Arrays.asList () возвращает список, который не допускает операций, влияющих на его размер (обратите внимание, что это не то же самое, что «неизменяемый»).

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

Вы хотите удалить list.size() - count (давайте назовем это k) случайные элементы из списка. Просто выберите столько случайных элементов и поменяйте их местами в конце k позиций списка, затем удалите весь этот диапазон (например, используя subList () и clear () для этого). Это превратит его в простой и средний алгоритм 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--;
  }
}
4 голосов
/ 03 июня 2010

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

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

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

EDIT

Тип результирующего списка - Arrays.ArrayList, который является закрытым классом внутри Arrays.class. На практике это не что иное, как представление списка в массиве, который вы передали с Arrays.asList. С последствием: если вы измените массив, список тоже изменится. А поскольку размер массива не изменяется, операция удаления и добавления должна не поддерживаться.

4 голосов
/ 03 июня 2010

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

List<String> list = new ArrayList(Arrays.asList(split));
3 голосов
/ 17 февраля 2015

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

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

работа на newList;)

2 голосов
/ 23 сентября 2013

Да, на 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 голосов
/ 03 июня 2010

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

...