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

时间:2010-06-03 12:08:44

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

我明白了:

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

17 个答案:

答案 0 :(得分:891)

您的代码存在一些问题:

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

来自API:

  

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

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

修复

创建LinkedList,支持更快remove

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

split上使用正则表达式

来自API:

  

String.split(String regex):将此字符串拆分为给定regular expression的匹配项。

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

修正:

template.split("\\|")

关于更好的算法

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

这样,您的算法将是remove()

答案 1 :(得分:121)

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

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

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

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

答案 2 :(得分:45)

可能是因为您正在使用unmodifiable wrapper

更改此行:

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

到这一行:

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

答案 3 :(得分:11)

我认为更换:

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

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

解决了这个问题。

答案 4 :(得分:4)

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

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

您想要从列表中删除list.size() - count(让我们调用此k)个随机元素。只需选择尽可能多的随机元素并将它们交换到列表的末尾k位置,然后删除整个范围(例如,使用subList()和clear())。这会将其变为精简且平均O(n)算法(O(k)更精确)。

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

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

答案 5 :(得分:4)

只需阅读JavaDoc for asList方法:

  

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

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

修改

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

答案 6 :(得分:4)

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

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

答案 7 :(得分:3)

我已经为这个问题找到了另一个解决方案:

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

处理newList;)

答案 8 :(得分:2)

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

答案 9 :(得分:2)

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

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

示例:

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

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

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

parentRecepeIdList.add("555");

答案 10 :(得分:1)

您无法删除,也无法添加固定大小的数组列表。

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

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

这将创建不像Arrays.asList

那样固定大小的ArrayList

答案 11 :(得分:1)

以下是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添加函数以将元素存储在数组中。因此,默认情况下,抽象列表中的add方法会抛出异常。

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

答案 12 :(得分:1)

替换

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

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

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

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

或(更好地删除元素)

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

答案 13 :(得分:0)

Arrays.asList()在内部使用固定大小的数组。
您不能为此动态添加或删除Arrays.asList()

使用此

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

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

答案 14 :(得分:0)

Arraylist narraylist = Arrays.asList(); //返回不可变的arraylist 使它可变的解决方案是: Arraylist narraylist = new ArrayList(Arrays.asList());

答案 15 :(得分:0)

创建新列表并在新列表中填充有效值对我来说很有效。

代码抛出错误-

List<String> list = new ArrayList<>();
   for (String s: list) {
     if(s is null or blank) {
        list.remove(s);
     }
   }
desiredObject.setValue(list);

修复后-

 List<String> list = new ArrayList<>();
 List<String> newList= new ArrayList<>();
 for (String s: list) {
   if(s is null or blank) {
      continue;
   }
   newList.add(s);
 }
 desiredObject.setValue(newList);

答案 16 :(得分:0)

问题是您使用固定长度的Arrays.asList()方法创建列表 意思是

由于返回的列表是固定大小的列表,因此我们无法添加/删除元素。

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

此迭代将产生异常,因为它是由asList()创建的迭代列表,因此无法进行删除和添加,它是一个固定数组

List<String> words = Arrays.asList("pen", "pencil", "sky", "blue", "sky", "dog"); 
for (String word : words) {
    if ("sky".equals(word)) {
        words.remove(word);
    }
}   

这将很好用,因为我们采用了新的ArrayList,我们可以在迭代时执行修改

List<String> words1 = new ArrayList<String>(Arrays.asList("pen", "pencil", "sky", "blue", "sky", "dog"));
for (String word : words) {
    if ("sky".equals(word)) {
        words.remove(word);
    }
}