【问题标题】:Why do I get an UnsupportedOperationException when trying to remove an element from a List?尝试从列表中删除元素时,为什么会收到 UnsupportedOperationException?
【发布时间】:2011-02-27 06:55:18
【问题描述】:

我有这个代码:

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

【问题讨论】:

  • 使用链表。
  • 对我来说这个错误是因为我试图修改collection.unmodifiablelist list

标签: java exception list arraylist


【解决方案1】:

你的代码有很多问题:

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,不如在该范围内生成足够多的随机数,然后使用listIterator() 遍历List 一次,在适当的索引处调用remove()。关于如何在给定范围内生成随机但不同的数字,stackoverflow 上有一些问题。

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

【讨论】:

  • 谢谢,我在字符串
  • @Pentium:还有一件事:您不应该每次都创建Random 的新实例。将其设为 static 字段并仅播种一次。
  • LinkedList 真的更快吗? LinkedList 和 ArrayList 都在这里删除了 O(n) :\ 使用 ArrayList 几乎总是更好
  • LinkedList vs ArrayList --> 有一张来自 Ryan 的性能测试图。 LinkedList 删除速度更快。
  • @drmrbrewer 你需要使用类的类型参数。 new LinkedList&lt;String&gt;(...)new LinkedList&lt;&gt;(...)(让编译器找出类型参数)。只是new LinkedList(...) 创建了一个“原始”(未参数化)链表,应该避免这种情况。
【解决方案2】:

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

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

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

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

【讨论】:

  • 次要问题,但您并没有“包装”原始列表,而是创建了一个全新的列表(这就是它起作用的原因)。
  • 是的,我在 JUnit 测试用例中使用了 Arrays.asList(),然后将其存储在我的地图中。更改了我的代码以将传入的列表复制到我自己的 ArrayList 中。
  • 您的解决方案不适用于我的情况,但感谢您的解释。您提供的知识促成了我的解决方案。
  • 它也适用于new ArrayList&lt;&gt;(Arrays.asList(...))
【解决方案3】:

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

改变这一行:

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

到这一行:

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

【讨论】:

  • Arrays.asList() 不是不可修改的包装器。
  • @polygenelubricants:看来你混淆了unmodifiableimmutableunmodifiable 的意思是“可修改,但不能在结构上”。
  • 我刚刚尝试创建一个unmodifiableList 包装器并尝试一个set;它抛出UnsupportedOperationException。我很确定 Collections.unmodifiable* 确实意味着完全不变,而不仅仅是结构性。
  • 在 7 年后阅读这些 cmets,我允许自己指出这个链接:stackoverflow.com/questions/8892350/… 可能会解决不可变和不可修改之间的区别,在这里讨论。
  • 是的,Array.asList 不是不可修改的包装器,“可修改,但在结构上不可修改”当然与“不可修改”不同。
【解决方案4】:

我认为替换:

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

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

解决问题。

【讨论】:

    【解决方案5】:

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

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

    【讨论】:

    • 他在删除,ArrayList 不是删除其值的最佳数据结构。 LinkedList 更能解决他的问题。
    • 关于链表的错误。他是按索引访问的,所以 LinkedList 会花费尽可能多的时间来通过迭代找到一个元素。请参阅我的答案以获得更好的方法,使用 ArrayList。
    【解决方案6】:

    只需阅读 asList 方法的 JavaDoc:

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

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

    编辑

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

    【讨论】:

      【解决方案7】:

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

      您可以使用new ArrayList&lt;String&gt;(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--;
        }
      }
      

      【讨论】:

      • +1 表示算法,尽管 OP 说只有 10 个元素。以及将随机数与ArrayList 一起使用的好方法。比我的建议简单得多。我认为这会导致元素重新排序。
      【解决方案8】:

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

      【讨论】:

        【解决方案9】:

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

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

        处理newList ;)

        【讨论】:

          【解决方案10】:

          替换

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

          【讨论】:

            【解决方案11】:

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

            由于返回的List是一个固定大小的List,我们不能添加/删除元素。

            查看下面我正在使用的代码块

            本次迭代会报异常,由于是迭代列表,由asList()创建,所以不能remove和add,是一个固定数组

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

            【讨论】:

            • 您好,这个答案基本上是正确的,但它似乎并没有为 10 多年前提供的现有答案/解决方案增加任何重要价值。
            【解决方案12】:

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

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

            示例:

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

            【讨论】:

              【解决方案13】:

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

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

              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

              【讨论】:

                【解决方案14】:

                Arraylist narraylist=Arrays.asList(); // 返回不可变数组列表 使其可变的解决方案是: Arraylist narraylist=new ArrayList(Arrays.asList());

                【讨论】:

                • 欢迎来到 SO。尽管我们感谢您的回答,但如果它在其他答案之上提供额外的价值会更好。在这种情况下,您的答案不会提供额外的价值,因为另一个用户已经发布了该解决方案。如果之前的答案对您有帮助,那么您应该在获得足够声誉后投票。
                【解决方案15】:

                以下是来自数组的代码的 sn-p

                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 方法会抛出异常。

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

                【讨论】:

                  【解决方案16】:

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

                  使用这个

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

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

                  【讨论】:

                    【解决方案17】:

                    创建一个新列表并在新列表中填充有效值对我有用。

                    代码抛出错误 -

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

                    【讨论】:

                      猜你喜欢
                      • 2013-04-01
                      • 2010-12-10
                      • 2018-02-07
                      • 2021-12-18
                      • 2012-03-17
                      • 2019-03-09
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多