Dlaczego podczas próby usunięcia elementu z listy pojawia się nieobsługiwane objęcie Operationexception?

Mam ten kod:

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

Rozumiem to:

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)

Jak to miałoby być właściwe? Java.15

Author: Pang, 2010-06-03

12 answers

Sporo problemów z Twoim kodem:

On Arrays.asList zwracanie listy o stałym rozmiarze

Z API:

Arrays.asList: zwraca Listę o stałym rozmiarze wspieraną przez podaną tablicę.

Nie możesz add do niego; nie możesz remove z niego. Nie można strukturalnie modyfikować List.

Fix

Utwórz LinkedList, który obsługuje szybciej remove.

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

On split biorąc regex

Z API:

String.split(String regex): dzieli ten łańcuch wokół dopasowania podanego wyrażenia regularnego .

| jest metacharakterem wyrażeń regularnych; jeśli chcesz podzielić na literał | , musisz go uciec do \|, który jako literał ciągu Java to "\\|".

Fix:

template.split("\\|")

O lepszym algorytmie

Zamiast wywoływać remove pojedynczo z losowymi indeksami, lepiej wygenerować wystarczającą liczbę losowych liczb w zakresie i następnie przemierzając List raz z listIterator(), wywołując remove() przy odpowiednich indeksach. Na stackoverflow pojawiają się pytania dotyczące sposobu generowania losowych, ale odrębnych liczb w danym zakresie.

Z tym, Twój algorytm będzie O(N).

 799
Author: polygenelubricants,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-03 12:19:15

Ten poparzył mnie wiele razy. Arrays.asList tworzy listę niemodyfikowaną. Z Javadoc: zwraca listę o stałym rozmiarze wspieraną przez podaną tablicę.

Utwórz nową listę o tej samej treści:

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

To stworzy trochę dodatkowych śmieci, ale będziesz w stanie je zmutować.

 110
Author: Nick Orton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-01 16:03:43

Prawdopodobnie dlatego, że pracujesz z niezmodyfikowanym opakowaniem .

Zmień tę linię:

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

Do tej linii:

List<String> list = new LinkedList<>(Arrays.asList(split));
 40
Author: Roman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-12 20:40:43

Myślę, że zastąpienie:

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

Z

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

Rozwiązuje problem.

 9
Author: Salim Hamidi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-24 18:29:52

Po prostu przeczytaj JavaDoc dla metody asList:

Zwraca {@lista kodów} obiektów w podanej tablicy. Wielkość nie można modyfikować {@code List} , czyli dodawanie i usuwanie są nieobsługiwane, ale elementy mogą być gotowi. Ustawienie elementu modyfikuje tablica bazowa.

To jest z Javy 6, ale wygląda na to, że jest to samo dla Javy android.

EDIT

Typem listy wynikowej jest Arrays.ArrayList, czyli Klasa prywatna wewnątrz tablic.klasy. Praktycznie rzecz biorąc, jest to nic innego jak widok listy na tablicy, którą przekazałeś Arrays.asList. W konsekwencji: jeśli zmienisz tablicę, zmieni się również lista. Ponieważ nie można zmienić rozmiaru tablicy, operacja Usuń i dodaj musi być nieobsługiwana.

 4
Author: Andreas_D,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-03 12:30:03

Tablice.asList () zwraca listę, która nie pozwala na operacje wpływające na jej rozmiar (zauważ, że nie jest to to samo co "unmodifiable").

Możesz zrobić new ArrayList<String>(Arrays.asList(split));, aby utworzyć prawdziwą kopię, ale widząc, co próbujesz zrobić, oto dodatkowa sugestia(masz algorytm O(n^2) tuż pod tym).

Chcesz usunąć list.size() - count (nazwijmy to k) losowe elementy z listy. Wystarczy wybrać tyle losowych elementów i zamienić je na koniec k pozycji listy, a następnie usuń cały zakres (np. używając subList () I clear ()). Dzięki temu algorytm O(n) jest bardziej precyzyjny (O(k)).

Update: Jak wspomniano poniżej, algorytm ten ma sens tylko wtedy, gdy elementy nie są uporządkowane, np. jeśli lista reprezentuje worek. Jeśli natomiast lista ma sensowną kolejność, algorytm ten nie zachowa jej (zamiast tego algorytm polygenelubricants).

Update 2: więc z perspektywy czasu lepiej (liniowy, zachowujący porządek, ale z O (n) liczbami losowymi) algorytm będzie wyglądał mniej więcej tak:

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
Author: Dimitris Andreou,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-03 13:03:53

Lista zwrócona przez Arrays.asList() może być niezmienna. Czy możesz spróbować

List<String> list = new ArrayList(Arrays.asList(split));
 4
Author: Pierre,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-12 20:41:20

Mam inne rozwiązanie tego problemu:

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

Praca na newList ;)

 3
Author: ZZ 5,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-12 20:40:58

Ta nieobsługiwana operacja pojawia się, gdy próbujesz wykonać jakąś operację na kolekcji, gdzie jej nie wolno, a w Twoim przypadku, gdy wywołujesz Arrays.asList nie zwraca java.util.ArrayList. Zwraca java.util.Arrays$ArrayList, która jest niezmienną listą. Nie można dodać do niego i nie można z niego usunąć.

 2
Author: Mayank Gupta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-03 13:30:40

Tak, na Arrays.asList, zwracając Listę o stałym rozmiarze.

Poza użyciem listy połączonej, po prostu użyj listy metod addAll.

Przykład:

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

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

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

parentRecepeIdList.add("555");
 2
Author: Sameer Kazi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-12 20:42:22

Poniżej znajduje się fragment kodu z tablic

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;

Więc dzieje się tak, że gdy metoda asList jest wywoływana, to zwraca listę własnej, prywatnej statycznej wersji klasy, która nie nadpisuje funkcji add z AbstractList do przechowywania elementu w tablicy. Tak więc domyślnie metoda add w liście abstrakcyjnej rzuca wyjątek.

Więc nie jest to zwykła lista tablic.

 1
Author: Gagandeep Singh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-15 04:10:13

Nie można usunąć, ani dodać do listy tablic o stałym rozmiarze.

Ale możesz utworzyć sublist z tej listy.

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

Spowoduje to utworzenie ArrayList, która nie ma stałego rozmiaru, jak tablice.asList

 1
Author: Venkat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-12 20:42:09