代码之家  ›  专栏  ›  技术社区  ›  Pentium10

尝试从列表中删除元素时,为什么会收到UnsupportedOperationException?

  •  398
  • Pentium10  · 技术社区  · 14 年前

    我有这个代码:

    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)
    

    这是怎么正确的方法?Java15

    13 回复  |  直到 6 年前
        1
  •  862
  •   polygenelubricants    14 年前

    您的代码有很多问题:

    Arrays.asList 返回固定大小列表

    从API:

    Arrays.asList 返回一个 固定尺寸表 由指定的数组支持。

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

    固定

    创建一个 LinkedList ,支持更快 去除 .

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

    split 采取正则表达式

    从API:

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

    | 是regex元字符;如果要在文本上拆分 γ 你必须逃到 \| ,作为Java字符串文字是 "\\|" .

    修复:

    template.split("\\|")
    

    关于更好的算法

    而不是打电话 去除 一次生成一个随机索引,最好在范围内生成足够的随机数,然后遍历 曾经与 listIterator() 呼唤 remove() 以适当的指数。StackOverflow中有一些关于如何在给定范围内生成随机但不同的数字的问题。

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

        2
  •  115
  •   damd    12 年前

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

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

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

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

        3
  •  43
  •   Andrii Abramov Mk.Sl.    7 年前

    可能是因为你和 unmodifiable wrapper .

    更改此行:

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

    这条线:

    List<String> list = new LinkedList<>(Arrays.asList(split));
    
        4
  •  11
  •   Jameson    8 年前

    我认为替换:

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

    具有

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

    解决问题。

        5
  •  4
  •   Andreas Dolk    14 年前

    只需阅读aslist方法的javadoc:

    返回对象的@代码列表 在指定的数组中。大小 @代码列表不能修改, 即添加和删除 不支持,但元素可以 集合。设置元素会修改 基础数组。

    这是来自Java 6,但它看起来与Android Java是一样的。

    编辑

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

        6
  •  4
  •   Dimitris Andreou    14 年前

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

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

    您要删除 list.size() - count (我们称之为 k )列表中的随机元素。只需选取任意多个元素并将它们交换到末尾 K 列表的位置,然后删除整个范围(例如,对其使用sublist()和clear())。那会使它变得很瘦 O(n) 算法(算法) O(k) 更精确)。

    更新 :如下文所述,此算法仅在元素无序时才有意义,例如,如果列表代表一个包。另一方面,如果列表有一个有意义的顺序,这个算法就不会保留它(相反,Polygene润滑剂的算法会保留它)。

    更新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--;
      }
    }
    
        7
  •  4
  •   Andrii Abramov Mk.Sl.    7 年前

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

    List<String> list = new ArrayList(Arrays.asList(split));
    
        8
  •  3
  •   Andrii Abramov Mk.Sl.    7 年前

    我有另一个解决办法:

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

    从事 newList ;)

        9
  •  2
  •   Mayank Gupta    14 年前

    当您尝试对不允许的集合执行某些操作时,以及在您的情况下,当您调用 Arrays.asList 它不返回 java.util.ArrayList . 它返回一个 java.util.Arrays$ArrayList 这是一个不变的列表。不能添加到其中,也不能从中删除。

        10
  •  2
  •   Andrii Abramov Mk.Sl.    7 年前

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

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

    例子:

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

    下面是来自数组的代码片段

    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 funcion。所以默认情况下,在抽象列表中添加方法会引发异常。

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

        12
  •  1
  •   Andrii Abramov Mk.Sl.    7 年前

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

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

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

    这将创建arraylist,其大小与arrays.aslist不同。

        13
  •  1
  •   Karthik Kompelli    6 年前

    替换

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