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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2965747/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 14:59:13  来源:igfitidea点击:

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

javaexceptionlistarraylist

提问by Pentium10

I have this code:

我有这个代码:

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, ", ");
}

I get this:

我明白了:

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)

How would be this the correct way? Java.15

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

采纳答案by polygenelubricants

Quite a few problems with your code:

你的代码有不少问题:

On Arrays.asListreturning a fixed-size list

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

From the API:

从API:

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

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

You can't addto it; you can't removefrom it. You can't structurally modify the List.

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

Fix

使固定

Create a LinkedList, which supports faster remove.

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

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


On splittaking regex

split接受正则表达式

From the API:

从API:

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

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

|is a regex metacharacter; if you want to split on a literal |, you must escape it to \|, which as a Java string literal is "\\|".

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

Fix:

使固定:

template.split("\|")


On better algorithm

关于更好的算法

Instead of calling removeone at a time with random indices, it's better to generate enough random numbers in the range, and then traversing the Listonce with a listIterator(), calling remove()at appropriate indices. There are questions on stackoverflow on how to generate random but distinct numbers in a given range.

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

With this, your algorithm would be O(N).

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

回答by Roman

Probably because you're working with unmodifiable wrapper.

可能是因为您正在使用不可修改的 wrapper

Change this line:

改变这一行:

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

to this line:

到这一行:

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

回答by Nick Orton

This one has burned me many times. Arrays.asListcreates an unmodifiable list. From the Javadoc: Returns a fixed-sizelist backed by the specified array.

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

Create a new list with the same content:

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

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

This will create a little extra garbage, but you will be able to mutate it.

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

回答by Pierre

The list returned by Arrays.asList()might be immutable. Could you try

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

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

回答by Andreas Dolk

Just read the JavaDoc for the asList method:

只需阅读 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.

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

This is from Java 6 but it looks like it is the same for the android java.

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

EDIT

编辑

The type of the resulting list is Arrays.ArrayList, which is a private class inside Arrays.class. Practically speaking, it is nothing but a List-view on the array that you've passed with Arrays.asList. With a consequence: if you change the array, the list is changed too. And because an array is not resizeable, remove and add operation mustbe unsupported.

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

回答by Dimitris Andreou

Arrays.asList() returns a list that doesn't allow operations affecting its size (note that this is not the same as "unmodifiable").

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

You could do new ArrayList<String>(Arrays.asList(split));to create a real copy, but seeing what you are trying to do, here is an additional suggestion (you have a O(n^2)algorithm right below that).

您可以new ArrayList<String>(Arrays.asList(split));创建一个真正的副本,但看看您要做什么,这里有一个额外的建议(您在其O(n^2)下方有一个算法)。

You want to remove list.size() - count(lets call this k) random elements from the list. Just pick as many random elements and swap them to the end kpositions of the list, then delete that whole range (e.g. using subList() and clear() on that). That would turn it to a lean and mean O(n)algorithm (O(k)is more precise).

您想从列表中删除list.size() - count(让我们称之为k)随机元素。只需选择尽可能多的随机元素并将它们交换到k列表的结束位置,然后删除整个范围(例如,在其上使用 subList() 和 clear() )。这将把它变成一个精益和平均的O(n)算法(O(k)更精确)。

Update: As noted below, this algorithm only makes sense if the elements are unordered, e.g. if the List represents a Bag. If, on the other hand, the List has a meaningful order, this algorithm would not preserve it (polygenelubricants' algorithm instead would).

更新:如下所述,该算法仅在元素无序时才有意义,例如,如果 List 表示一个 Bag。另一方面,如果 List 具有有意义的顺序,则该算法不会保留它(polygenelubricants 的算法会)。

Update 2: So in retrospect, a better (linear, maintaining order, but with O(n) random numbers) algorithm would be something like this:

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

回答by Mayank Gupta

This UnsupportedOperationException comes when you try to perform some operation on collection where its not allowed and in your case, When you call Arrays.asListit does not return a java.util.ArrayList. It returns a java.util.Arrays$ArrayListwhich is an immutable list. You cannot add to it and you cannot remove from it.

当您尝试在不允许的集合上执行某些操作时,会出现此 UnsupportedOperationException ,并且在您的情况下,当您调用Arrays.asList它时不会返回java.util.ArrayList. 它返回一个java.util.Arrays$ArrayList不可变列表。您无法添加到其中,也无法从中删除。

回答by Venkat

You can't remove, nor can you add to a fixed-size-list of Arrays.

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

But you can create your sublist from that list.

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

list = list.subList(0, list.size() - (list.size() - count));

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, ", ");
}

*Other way is

*另一种方式是

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

this will create ArrayList which is not fixed size like Arrays.asList

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

回答by Salim Hamidi

I think that replacing:

我认为替换:

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

with

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

resolves the problem.

解决问题。

回答by Sameer Kazi

Yes, on Arrays.asList, returning a fixed-size list.

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

Other than using a linked list, simply use addAllmethod list.

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

Example:

例子:

String idList = "123,222,333,444";

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

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

parentRecepeIdList.add("555");