尝试从List中删除元素时,为什么会出现UnsupportedOperationException?

Pen*_*m10 444 java exception list arraylist

我有这个代码:

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, ", ");
}
Run Code Online (Sandbox Code Playgroud)

我明白了:

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)
Run Code Online (Sandbox Code Playgroud)

这怎么会是正确的方法?Java.15

pol*_*nts 945

你的代码有很多问题:

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

来自API:

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

你不能add这样; 你不能remove从它.你不能在结构上修改List.

固定

创建一个LinkedList,支持更快remove.

List<String> list = new LinkedList<String>(Arrays.asList(split));
Run Code Online (Sandbox Code Playgroud)

关于split正则表达式

来自API:

String.split(String regex):围绕给定正则表达式的匹配拆分此字符串.

|是一个正则表达式元字符; 如果要拆分文字|,则必须将其转义\|为Java字符串文字"\\|".

固定:

template.split("\\|")
Run Code Online (Sandbox Code Playgroud)

更好的算法

不要remove一次使用随机索引调用一个,最好在范围内生成足够的随机数,然后List使用a 遍历一次listIterator(),调用remove()适当的索引.关于如何在给定范围内生成随机但不同的数字的stackoverflow存在问题.

有了这个,你的算法就是O(N).

  • @Pentium:还有一件事:你不应该每次都创建一个新的`Random`实例.使它成为一个"静态"字段,只播种一次. (6认同)
  • LinkedList真的更快吗?LinkedList和ArrayList都在这里删除O(n):\使用ArrayList几乎总是更好 (6认同)
  • [LinkedList vs ArrayList](/sf/ask/22590081/) - > Ryan有一个性能测试图.LinkedList在删除方面更快. (2认同)

Nic*_*ton 134

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

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

newList.addAll(Arrays.asList(newArray));
Run Code Online (Sandbox Code Playgroud)

这会产生一些额外的垃圾,但你可以改变它.

  • 小点,但你没有"包装"原始列表,你正在创建一个全新的列表(这就是它工作的原因). (6认同)

Rom*_*man 48

可能是因为你正在使用不可修改的包装器.

改变这一行:

List<String> list = Arrays.asList(split);
Run Code Online (Sandbox Code Playgroud)

到这一行:

List<String> list = new LinkedList<>(Arrays.asList(split));
Run Code Online (Sandbox Code Playgroud)

  • Arrays.asList()不是不可修改的包装器. (5认同)
  • 我只是尝试创建一个`unmodifiableList`包装器并尝试`set`; 它抛出`UnsupportedOperationException`.我很确定`Collections.unmodifiable*`真的意味着完全不变,而不仅仅是结构. (2认同)

Sal*_*idi 13

我认为更换:

List<String> list = Arrays.asList(split);
Run Code Online (Sandbox Code Playgroud)

List<String> list = new ArrayList<String>(Arrays.asList(split));
Run Code Online (Sandbox Code Playgroud)

解决了这个问题.


小智 7

问题是您正在使用 Arrays.asList() 方法创建一个具有固定长度的列表\n这意味着

\n
\n

由于返回的 List 是固定大小的 List,因此我们可以\xe2\x80\x99t 添加/删除元素。

\n
\n

请参阅下面我正在使用的代码块

\n
\n

这次迭代将给出异常,因为它是由 asList() 创建的迭代列表,所以不可能删除和添加,它是一个固定数组

\n
\n
List<String> words = Arrays.asList("pen", "pencil", "sky", "blue", "sky", "dog"); \nfor (String word : words) {\n    if ("sky".equals(word)) {\n        words.remove(word);\n    }\n}   \n
Run Code Online (Sandbox Code Playgroud)\n
\n

这会工作得很好,因为我们正在使用一个新的 ArrayList,我们可以在迭代时执行修改

\n
\n
List<String> words1 = new ArrayList<String>(Arrays.asList("pen", "pencil", "sky", "blue", "sky", "dog"));\nfor (String word : words) {\n    if ("sky".equals(word)) {\n        words.remove(word);\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n


Pie*_*rre 5

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

List<String> list = new ArrayList(Arrays.asList(split));
Run Code Online (Sandbox Code Playgroud)

  • 关于LinkedList错误。他正在按索引访问,因此LinkedList将花费大量时间通过迭代查找元素。请参阅我的答案,以使用ArrayList获得更好的方法。 (2认同)

And*_*s_D 5

只需阅读 asList 方法的 JavaDoc:

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

这是来自 Java 6,但它看起来与 android java 相同。

编辑

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