Unsupported Operation on AbstractList.add() - java

I got a method
foo(list);
that get's a
List<SomeEntit>
as input.
My method foo looks somewhat like the following:
public void foo(List<SomeEntity someEntities) {
someEntities.add(anotherEntity);
}
I then get an "javax.ejb.EJBException: java.lang.UnsupportedOperationException" caused by "java.lang.UnsupportedOperationException: null" at "at java.util.AbstractList.add(AbstractList.java:148)"
Can you tell me why this is happening? I hope that my code example is not too minimal.

Some lists are unmodifiable. The operation of adding elements is then "unsupported".
Java collections framework does not have a distinct type for unmodifiable lists or other unmodifiable collections. You never really know if it is allowed to add something.
All you can do is to specify that the list that is passed must be modifiable.

It seems that the actual type of the List you get as the input does not override the add method.
Try converting that list to a list implementation that does, like ArrayList:
List<SomeEntity> newList = new ArrayList<>(list);
foo(newList);

Related

java.lang.UnsupportedOperationException with ArrayList()

I have a class like this
#Getter
public class MyClass {
private Collection<String> headers;
public myConfig(DownloadRequest downloadRequest) {
this.headers = downloadRequest.getHeaders() == null ? new ArrayList() : downloadRequest.getHeaders();
}
}
When I run this it gives me java.lang.UnsupportedOperationException.
As I use headers in another function and do getHeaders().clear(), I get this error.
Return type of downloadRequest.getHeaders() is Collection<String>
I am unable to figure out what I can cast the headers to.
I keep getting different Exceptions as I change my code, like UnsupportedOperationException and java.lang.ClassCastException: java.util.Arrays$ArrayList incompatible with java.util.ArrayList, when I change the code to something else, trying out other StackOverflow solutions like this - Why do I get an UnsupportedOperationException when trying to remove an element from a List?.
I have just started working on Java and have been working more on python and nodejs in the past.
Any help is appreciated.
The problem is that Collection#clear(), as mentioned in the docs is an optional operation and not all implementations need to support it. If they do not, they can throw UnsupportedOperationException. In your case, the list returned from downloadRequest.getHeaders() is apparently an unmodifiable collection which does not support this operation.
If it did, calling clear() on it would remove all headers from the downloadRequest. Is that what you're trying to achieve? Probably not. If yes, instead there should be a method like downloadRequest.clearHeaders().
To work around this problem, you can copy the elements out of an unmodifiable collection into e.g. an ArrayList:
new ArrayList<>(downloadRequest.getHeaders())
This copies the elements out of the original container into a new modifiable ArrayList that you can then modify however you like.
Also note you're mentioning java.util.Arrays$ArrayList which is named ArrayList, but it is not java.util.ArrayList. This is an unresizable collection returned from Arrays.asList(...) and it does not support clear() either.
The downloadRequest.getHeaders() method may return a Collection that is read-only and so doesn't support .clear()
You may use an ArrayList to wrap it and get a write-accessible structure
// private list<String> headers;
Collection<String> headers = downloadRequest.getHeaders();
this.headers = headers == null ? new ArrayList<>() : new ArrayList<>(headers) ;
I think you may be using the wrong ArrayList class when doing new ArrayList()
From your ClassCastException you are using :
java.util.Arrays$ArrayList, which is a nested class of the Arrays class and is immutable.
Check your import : you should be using java.util.ArrayList.
Edit : never mind this nested class is private; previous answer is probably right, the collection returned must be immutable.

how to remove element from List in java

This is reg. a requirement where I need to remove an element from List in java. I am getting unsupported exception when I try to remove element from List. Below is the code:
String[] str_array = {"abc","def","ght"};
List<String> results = Arrays.asList(str_array);
String tobeRemovedItem="";
for(int i=0;i<results.size();i++){
if(results.get(i).equalsIgnoreCase(searchString)) {
tobeRemovedItem=results.get(i);
}
}
if(!TextUtils.isEmpty(tobeRemovedItem)) {
results.remove(tobeRemovedItem); // I am getting exception here.
}
Can anyone help me in solving this issue?
The type of list returned by Arrays.asList does not support the remove operation. Hence the exception.
You can use the java.util.ArrayList instead.
List<String> results = new ArrayList<String>(Arrays.asList(str_array));
Answered already, but now without indirect datastructure of .asList()
List<String> results = new ArrayList<>();
Collections.addAll(results, str_array);
The .asList is backed by the array, hence you can modify the original array be modifying the list. And vice versa you cannot grow or shrink the list, as then the backed array object would need to be exchanged, as arrays are fixed in java.
The size of List returned by Arrays.asList cannot be changed. Instead you can do:
List<String> results = new ArrayList<>(Arrays.asList(str_array));
In general, UnsupportedOperationException is thrown by the implementation of an interface (or a child class of a class with an abstract method), where the implementor did not want to support that particular method.
That said, to debug these issues in the future, check which implementation you're using - in this case, it's given via the Arrays.asList() method from the Android sdk. Here you can see it says that it does not support adding or removing of items to the list.
If you must add and remove items, you can wrap the call into the ArrayList implementation of List which does support such modification (as suggested by Banthar and khelwood). The constructor takes a list as input, and copies the elements inside.

What is the use of singletonList?

I was looking around for some elegant solution to removing null values from a List. I came across the following post, which says I can use list.removeAll(Collections.singletonList(null));
This, however, throws an UnsupportedOperationException, which I'm assuming is because removeAll() is attempting to do some mutative operation on the immutable singleton collection. Is this correct?
If this is the case, what would be a typical use of this singletonList? To represent a collection of size 1 when you're sure you don't want to actually do anything with the collection?
Thanks in advance.
It works like a charm:
List<String> list = new ArrayList<String>();
list.add("abc");
list.add(null);
list.add("def");
list.removeAll(Collections.singletonList(null));
System.out.println(list); //[abc, def]
Indeed Collections.singletonList(null) is immutable (which is unfortunately hidden in Java[1]), but the exception is thrown from your list variable. Apparently it is immutable as well, like in example below:
List<String> list = Arrays.asList("abc", null, "def");
list.removeAll(Collections.singletonList(null));
This code will throw an UnsupportedOperationException. So as you can see singletonList() is useful in this case. Use it when client code expects a read-only list (it won't modify it) but you only want to pass one element in it. singletonList() is (thread-)safe (due to immutability), fast and compact.
[1] E.g. in scala there is a separete hierarchy for mutable and immutable collections and API can choose whether it accept this or the other (or both, as they have common base interfaces)
To answer your actual question :
what would be a typical use of this singletonList? To represent a collection of size 1 when you're sure you don't want to actually do anything with the collection?
The typical use is if you have one element and want to pass it to a method that accepts a List, ie
public void registerUsers(List<User> users) {...}
User currentUser = Login Manager.getCurrentUser();
registerUsers(Collections.singletonList(currentUser));
The removeAll() is a special case for this.
Has your list been protected with
Collections.unmodifiableList(list)
Because if you have protected it and try to modify it later you get that error.

Arrays.asList() doubt?

People say that asList method convert the array into list and its not copying, so every change in 'aList' will reflect into 'a'. So add new values in 'aList' is illegal, since array have fixed size.
But, asList() method returns ArrayList<T>. How the compiler differentiates line 3 from 5. Line 3 gives me exception (UnsupportedOperationException).
String[] a = {"a","b","c","d"};//1
List<String> aList = Arrays.asList(a);//2
aList.add("e");//3
List<String> b = new ArrayList<String>();//4
b.add("a");//5
This List implementation you receive from Arrays.asList is a special view on the array - you can't change it's size.
The return type of Arrays.asList() is java.util.Arrays.ArrayList which is often confused with java.util.ArrayList. Arrays.ArrayList simply shows the array as a list.
Read again, the type of Arrays.asList is:
public static <T> List<T> asList(T... a)
which clearly states that asList returns an object that implements interface java.util.List, nowhere does it says it will return an instance of class java.util.ArrayList.
Next, notice that the documentation on List.add says:
boolean add(E e)
Appends the specified element to the end of this list (optional operation).
Technically, everytime you use a variable typed as List (instead of ArrayList), you should always be careful to expect that this method may throw UnsupportedOperationException. If you are sure that you will only receive a List implementation that always have the correct semantic of .add(), then you can omit the check at the risk of a bug when your assumption is invalidated.
Manoj,
The Return type of Arrays.List is some unknown internal implementation of the List interface and not java.util.ArrayList, so you can assign it only to a List type.
If you assign it to an ArrayList for instance it will give you compile time error
"Type mismatch: cannot convert from List to ArrayList"
ArrayList<String> aList = Arrays.asList(a);// gives Compile time error
From the Javadoc "Arrays.asList Returns a fixed-size list backed by the specified array. (Changes to the returned list "write through" to the array.) " that means that you are only provided a list view of the Array which IMO is created at runtime and ofcourse you cannot change the size of an array so you can't change size of "Arrays.asList" also.
IMO the internal implementation of Arrays.asList has all the implemented methods which can change the size of the Array as -
void add(E e)
{
//some unknown code
throw(java.lang.UnsupportedOperationException);
}
so whenever you attempt to alter the size of the Array it throws the UnsupportedOperationException.
Still if you want to add some new items to an ArrayList by using such a syntax, you can do so by creating a subclass of Arraylist(preferably by using anonymous subclass of ArrayList). You can pass the return type of Arrays.List to the constructor of ArrayList, (ie. public ArrayList(Collection c)) something like this -
List<String> girlFriends = new java.util.ArrayList<String>(Arrays.asList("Rose", "Leena", "Kim", "Tina"));
girlFriends.add("Sarah");
Now you can easily add Sarah to your GF list using the same syntax.
PS - Please select this one or another one as your answer because evrything has been explained. Your low Acceptance rate is very discouraging.
asList() doesn't return a java.util.ArrayList, it returns a java.util.Arrays$ArrayList. This class doesn't even extend java.util.ArrayList, so its behaviour can be (and is) completely different.
The add() method is inherited from java.util.AbstractList, which by default just throws UnsupportedOperationException.
You're assuming that Arrays.asList() returns an ArrayList, but that's not the case. Arrays.asList() returns an unspecified List implementation. That implementaton simply throws an UnsupportedOperationException on each unsupported method.
It's an exception and not a compiler error. It is thrown when the program is run and not at the compile time. Basically the actual class that Arrays.asList will return has a throw UnsupporteOperationException inside the add() method.
To be more specific Arrays.asList will return an inner class defined inside the Arrays class that is derived from AbstractList and does not implement the add method. The add method from the AbstractList is actually throwing the exception.
The key to this is the List implementation returned by
List<String> aList = Arrays.asList(a);
If you look at the source code in Arrays you will see that it contains an internal private static class ArrayList. This is not the same as java.util.ArrayList.
asList returns a fixed-size list, so that you cannot add new elements to it. Because the list it returns is really a "view" of the array it was created from ('a' in your case), it makes sense that you won't be able to add elements - just like you can't add elements to an array. See the docs for asList

Use of contains in Java ArrayList<String>

If I have an ArrayList of String forming part of a class in Java like so:
private ArrayList<String> rssFeedURLs;
If I want to use a method in the class containing the above ArrayList, using ArrayList contains to check if a String is contained in this ArrayList, I believe I should be able to do so as follows:
if (this.rssFeedURLs.contains(rssFeedURL)) {
Where rssFeedURL is a String.
Am I right or wrong?
You are right. ArrayList.contains() tests equals(), not object identity:
returns true if and only if this list
contains at least one element e such
that (o==null ? e==null : o.equals(e))
If you got a NullPointerException, verify that you initialized your list, either in a constructor or the declaration. For example:
private List<String> rssFeedURLs = new ArrayList<String>();
Yes, that should work for Strings, but if you are worried about duplicates use a Set. This collection prevents duplicates without you having to do anything. A HashSet is OK to use, but it is unordered so if you want to preserve insertion order you use a LinkedHashSet.
You are right that it should work; perhaps you forgot to instantiate something. Does your code look something like this?
String rssFeedURL = "http://stackoverflow.com";
this.rssFeedURLS = new ArrayList<String>();
this.rssFeedURLS.add(rssFeedURL);
if(this.rssFeedURLs.contains(rssFeedURL)) {
// this code will execute
}
For reference, note that the following conditional will also execute if you append this code to the above:
String copyURL = new String(rssFeedURL);
if(this.rssFeedURLs.contains(copyURL)) {
// code will still execute because contains() checks equals()
}
Even though (rssFeedURL == copyURL) is false, rssFeedURL.equals(copyURL) is true. The contains method cares about the equals method.
Perhaps you need to post the code that caused your exception. If the above is all you have, perhaps you just failed to actually initialise the array.
Using contains here should work though.
Your question is not very clear.
What's your code exactly doing? Give more code.
What's the error you're getting?
You say you get a null-pointer. You cannot get a null pointer as a value returned by contains().
However you can get a NullPointerException if your list has not been initialized. By reading your question now, I'd say that what you show here is correct, but maybe you just didn't instantiate the list.
For this to work (to add a feed URL if it isn't already in the list):
if (!this.rssFeedURLs.contains(rssFeedURL)) {
this.rssFeedURLs.add(rssFeedUrl);
}
then this declaration would do:
private ArrayList<String> rssFeedURLs = new ArrayList<String>();
or initialize your list later on, but before trying to access its methods:
rssFeedUrls = new ArrayList<String>();
Finally... Do you really need a List? Maybe a Set would be better if you don't want duplicates. Use a LinkedHashSet if preserving the ordering matters.
Right...with strings...the moment you deviate from primitives or strings things change and you need to implement hashcode/equals to get the desired effect.
EDIT: Initialize your ArrayList<String> then attempt to add an item.
You're correct. As others said according to your comments, you probably did not initialize your ArrayList.
My point is different: you claimed that you're checking for duplicates and this is why you call the contains method. Try using HashSet. It should be more efficient - unless you need to keep the order of URLs for any reason.
Thanks to you all for answering so quickly. I could always use a set but I have the ArrayList working now. The problem was that in the constructor of the class containing the ArrayList, I was not saying:
public RSS_Feed_Miner() {
...
this.rssFeedURLs = new ArrayList<String>();
...
}
D'Oh! for a Friday afternoon.
ArrayList<String> newlyAddedTypes=new ArrayList<String>();
.....
newlyAddedTypes.add("test1");
newlyAddedTypes.add("test1");
newlyAddedTypes.add("test2");
if(newlyAddedTypes.contain("test"){
//called here
}
else{
}

Categories

Resources