Why do I get an UnsupportedOperationException when trying to remove an element from a List?
I have this code:
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, ", ");
}
I get this:
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)
How would be this the correct way? Java.15
Quite a few problems with your code:
On Arrays.asList
returning a fixed-size list
From the API:
Arrays.asList
: Returns a fixed-size list backed by the specified array.
You can't add
to it; you can't remove
from it. You can't structurally modify the List
.
Fix
Create a LinkedList
, which supports faster remove
.
List<String> list = new LinkedList<String>(Arrays.asList(split));
On split
taking regex
From the API:
String.split(String regex)
: Splits this string around matches of the given regular expression.
|
is a regex metacharacter; if you want to split on a literal |
, you must escape it to \|
, which as a Java string literal is "\\|"
.
Fix:
template.split("\\|")
On better algorithm
Instead of calling remove
one at a time with random indices, it's better to generate enough random numbers in the range, and then traversing the List
once with a listIterator()
, calling remove()
at appropriate indices. There are questions on stackoverflow on how to generate random but distinct numbers in a given range.
With this, your algorithm would be O(N)
.
This one has burned me many times. Arrays.asList
creates an unmodifiable list.
From the Javadoc: Returns a fixed-size list backed by the specified array.
Create a new list with the same content:
newList.addAll(Arrays.asList(newArray));
This will create a little extra garbage, but you will be able to mutate it.
Probably because you're working with unmodifiable wrapper.
Change this line:
List<String> list = Arrays.asList(split);
to this line:
List<String> list = new LinkedList<>(Arrays.asList(split));