为什么在尝试从列表中删除元素时会得到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) 

这将是怎样的正确的方式? Java.15

你的代码有很多问题:

Arrays.asList返回一个固定大小的列表

从API:

Arrays.asList :返回由指定数组支持的固定大小的列表

你不能add到它; 你不能remove它。 您不能从结构上修改List

固定

创build一个LinkedList ,支持更快的remove

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

split正则expression式

从API:

String.split(String regex) :围绕给定正则expression式的匹配拆分这个string。

| 是一个正则expression式元字符; 如果你想分裂一个字面| ,你必须将其转义为\| ,作为Javastring文字是"\\|"

固定:

 template.split("\\|") 

在更好的algorithm

而不是每次调用随机索引remove一个,最好在范围内生成足够的随机数,然后用listIterator()遍历List ,并在适当的索引处调用remove() 。 有关如何在给定的范围内产生随机但不同的数字的计算器上的问题。

有了这个,你的algorithm将是O(N)

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

用相同的内容创build一个新的列表:

 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)); 

解决了这个问题。

只要阅读asList方法的JavaDoc:

返回指定数组中对象的一个​​列表。 {@code List}的大小不能修改,即不支持添加和删除,但可以设置元素。 设置一个元素可以修改底层数组。

这是从Java 6,但它看起来像是相同的Android Java。

编辑

结果列表的types是Arrays.ArrayList ,它是Arrays.ArrayList中的一个私有类。 实际上,这只不过是您用Arrays.asList传递的数组上的List-view。 结果是:如果更改数组,则列表也会更改。 而且由于数组不可resize,删除和添加操作必须不受支持。

Arrays.asList()返回一个不允许操作影响其大小的列表(请注意,这与“不可修改”不一样)。

你可以做new ArrayList<String>(Arrays.asList(split)); 创build一个真正的副本,但看到你正在做什么,这里是一个额外的build议(你有一个O(n^2)algorithm正下方)。

你想从列表中移除list.size() - count (让我们调用这个k )个随机元素。 只要select很多随机元素,将它们交换到列表的最后k位置,然后删除整个范围(例如,使用subList()和clear())。 这将把它变成一个精益和意味着O(n)algorithm( O(k)更精确)。

更新 :如下所述,如果元素是无序的,例如如果List代表一个Bag,这个algorithm才有意义。 另一方面,如果列表有一个有意义的顺序,这个algorithm将不会保留它(polygenelubricantsalgorithm反而会)。

更新2 :所以回想起来,一个更好的(线性,维持秩序,但与O(n)随机数)algorithm将是这样的:

 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--; } } 

我有另一个解决这个问题的方法:

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

newList工作)

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

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

这个UnsupportedOperationException是当你试图对不允许的集合执行一些操作的时候,在你的情况下,当你调用Arrays.asList它不会返回一个java.util.ArrayList 。 它返回一个java.util.Arrays$ArrayList ,它是一个不可变列表。 你不能添加到它,你不能删除它。

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

但是您可以从该列表创build您的子列表。

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)); 

这将创buildArrayList,它不像Arrays.asList那样是固定大小的

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

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

例:

 String idList = "123,222,333,444"; List<String> parentRecepeIdList = new ArrayList<String>(); parentRecepeIdList.addAll(Arrays.asList(idList.split(","))); parentRecepeIdList.add("555"); 

以下是来自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; 

所以会发生的是,当asList方法被调用,然后它返回自己的私人静态类版本的列表,它不会覆盖从AbstractList添加funcion存储元素在数组中。 所以默认情况下,抽象表中的add方法会抛出exception。

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