关于java:为什么在尝试从List中删除元素时会出现UnsupportedOperationException?

Why do I get an UnsupportedOperationException when trying to remove an element from a List?

我有这个代码:

1
2
3
4
5
6
7
8
9
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,",");
}

我明白了:

1
2
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


您的代码有很多问题:

返回固定大小列表

从API:

Arrays.asList: Returns a fixed-size list backed by the specified array.

你不能用它来对付它;你不能用它来对付它。您不能在结构上修改List

固定

创建一个LinkedList,它支持更快的remove

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

。关于split服用regex

从API:

String.split(String regex): Splits this string around matches of the given regular expression.

EDCOX1·18是一个正则表达式元;如果你想在一个EDCOX1的文本18中分离,你必须将它转至EDCOX1×20,这是一个Java字符串文字是EDCOX1,21。

修复:

1
template.split("\\|")

。关于更好的算法

与其一次调用一个随机索引的remove,不如在范围内生成足够的随机数,然后用listIterator()遍历一次List,在适当的索引处调用remove()。StackOverflow中有一些关于如何在给定范围内生成随机但不同的数字的问题。

有了这个,您的算法将是O(n)


这个把我烧了很多次。Arrays.asList创建一个不可修改的列表。从javadoc:返回由指定数组支持的固定大小列表。

创建具有相同内容的新列表:

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

这将产生一点额外的垃圾,但您将能够对其进行变异。


可能是因为您使用的是不可修改的包装器。

更改此行:

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

到此行:

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


我认为替换:

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

具有

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

解决问题。


arrays.as list()返回一个不允许操作影响其大小的列表(请注意,这与"不可修改"不同)。

你可以做new ArrayList(Arrays.asList(split));来创建一个真实的拷贝,但是看看你想做什么,这里有一个额外的建议(你在下面有一个O(n^2)算法)。

您想从列表中删除list.size() - count(我们称之为k)随机元素。只需选取任意多个元素,并将它们交换到列表的末尾k位置,然后删除整个范围(例如,对其使用sublist()和clear())。这将使它变成一个精简的平均O(n)算法(O(k)更精确)。

更新:如下面所述,此算法仅在元素无序时才有意义,例如,如果列表代表一个包。另一方面,如果列表有一个有意义的顺序,这个算法就不会保留它(相反,Polygene润滑剂的算法会保留它)。

更新2:所以回顾一下,一个更好的(线性的,保持顺序的,但是有O(N)随机数)算法应该是这样的:

1
2
3
4
5
6
7
8
9
10
11
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--;
  }
}


只需阅读aslist方法的javadoc:

Returns a {@code List} of the objects
in the specified array. The size of
the {@code List} cannot be modified,
i.e. adding and removing are
unsupported, but the elements can be
set. Setting an element modifies the
underlying array.

这是来自Java 6,但它看起来与Android Java是一样的。

编辑

结果列表的类型是Arrays.ArrayList,它是array.class内部的私有类。实际上,它只是您通过Arrays.asList传递的数组上的一个列表视图。结果是:如果更改数组,列表也会更改。而且,由于数组不可调整大小,因此必须不支持删除和添加操作。


Arrays.asList()返回的列表可能是不可变的。你能试试吗

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


我有另一个解决办法:

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

newList上工作;


是的,在Arrays.asList上,返回一个固定大小的列表。

除了使用链表外,只需使用addAll方法列表。

例子:

1
2
3
4
5
6
7
String idList ="123,222,333,444";

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

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

parentRecepeIdList.add("555");


当您尝试对不允许的托收执行某些操作时,就会出现这种不受支持的操作异常;在您的情况下,当您调用Arrays.asList时,它不会返回java.util.ArrayList。它返回一个java.util.Arrays$ArrayList,它是一个不变的列表。您不能添加到其中,也不能从中删除。


更换

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

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

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

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

或(更适合于删除元素)

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


下面是来自数组的代码片段

1
2
3
4
5
6
7
8
9
10
11
12
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方法时,它会返回它自己的私有静态类版本的列表,这个版本不会覆盖从abstractlist向数组中存储元素的add funcion。所以默认情况下,在抽象列表中添加方法会引发异常。

所以它不是常规的数组列表。


不能删除,也不能添加到固定大小的数组列表中。

但您可以从该列表创建子列表。

江户十一〔30〕

1
2
3
4
5
6
7
8
9
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,",");
}

*另一种方法是

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

这将创建arraylist,其大小与arrays.aslist不同。


Arrays.asList()在内部使用固定大小的数组。您不能动态地从thisEDOCX1中添加或删除[0]

使用这个

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

narraylist中,您可以轻松地添加或删除项目。