为什么在尝试从列表中删除元素时会得到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。
所以它不是常规的数组列表。