首页 文章

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

提问于
浏览
373

我有这个代码:

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

13 回答

  • 4

    以下是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方法会抛出异常 .

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

  • 1

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

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

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

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

  • 4

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

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

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

  • 2

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

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

    Example:

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

    可能是因为你正在使用unmodifiable wrapper .

    改变这一行:

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

    到这一行:

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

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

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

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

    Update :如下所述,该算法仅在元素无序时才有意义,例如:如果List代表Bag . 另一方面,如果List具有有意义的顺序,则该算法不会保留它(polygenelubricants的算法将改为) .

    Update 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--;
      }
    }
    
  • 4

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

    But you can create your sublist from that list.

    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

  • 3

    你的代码有很多问题:

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

    来自API:

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

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

    修复

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

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

    关于拆分采用正则表达式

    来自API:

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

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

    修复:

    template.split("\\|")
    

    关于更好的算法

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

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

  • 2

    我认为更换:

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

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

    解决了这个问题 .

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

    只需阅读JavaDoc for asList方法:

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

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

    EDIT

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

  • 112

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

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

    工作 newList ;)

相关问题