I have a code that read list from some paged string data. What I do not understand - why the UnsupportedOperationException is thrown on addAll() and why it's kind of random behaviour ?
I know creating target ArrayList and not adding to the returned one solves the issue, I'm looking for better understanding not a fix.
List<Event> eventList = eventTable.getEvents(); // returns ArrayList
while (hasNextPage()) {
goToNextPage();
eventList.addAll(eventTable.getEvents());
}
List<Event> is not necessarily an ArrayList<Event>. (The opposite is true though.)
The reason you get UnsupportedOperationException sometimes, is because eventTable.getEvents() sometimes returns a list that supports addAll and sometimes it doesn't.
The implementation of getEvents could for instance look like this:
if (noEventsAvailable) {
return Collections.emptyList();
} else {
List<Event> toReturn = new ArrayList<Event>();
// populate list...
return toReturn;
}
(In your comment you write // returns ArrayList. I don't know where you've got this from, but I know one thing for sure: An ArrayList will always support the addAll operation.)
The correct way to solve it is, as you mention, to do
List<Event> eventList = new ArrayList<Event>(eventTable.getEvents());
It depends on the actual implementation of List.
e.g if the underlying list was obtained using Collections.unmodifiableList() then calling addAll() or any other modification method will throw an UnsupportedOperationException.
When it throws an exception, it should show you the exact line number and source code file - you should be able to find out exactly why it's throwing an exception.
My guess is that under certain circumstances, eventTable.getEvents() returns an immutable list, or something like that - but without knowing what eventTable is, it's hard to say for sure. If you can produce a short but complete program which demonstrates the problem, that would make it a lot easier to diagnose.
Related
I have a below code
item.getSubCategory().stream().map(category -> {
return subCategoriesList.add(new SubCategoryViewModel(
category.get(String.format("_%s",categoryProperties[0])).toString(),
category.get(categoryProperties[2]).toString(),
category.get(categoryProperties[3]).toString()));
}).collect(Collectors.toList());
The item.getSubCategory() is a list of category model. Now if null appears on the getSubCategory we get an null pointer exception and can't apply steam to it. Is there a better way to handle null check to the list before applying stream to it.
I don't want to use IF statement to check null on getSubCategory. Is there any better way in java steam API ?
A method returning a list should never return null - it is a very bad practice. Just initialize the list in the constructor and return an empty one if there aren't any objects added to it. This way you eliminate all null checks that are needed whenever you want to use getSubCategory.
One could say that you end up with an unused object that takes memory. However, unless you have really a lot of item objects, having a few "spare" lists won't hurt anyone. Remember that code readability comes first in most cases - fix performance when you see actual problems.
Edit: as suggested by Holger, if you're worried about the memory cost of those empty lists, you could use Collections.emptyList(). Inside of your item you keep everything as it was, create the list only when it is needed and such. However, inside of getSubCategory instead of returning null you should check if the list is initialized and if it isn't then return Collections.emptyList() in place of null. According to documentation, Collections.emptyList() returns an immutable instance of a list, and multiple calls will return the same instance - only one will ever be created.
Starting with Java 9, you can also make use of Objects.requireNonNullElse():
Objects.requireNonNullElse(item.getSubCategory(), Collections.emptyList())
.stream()
.map(...);
This may look similar to:
Optional.ofNullable(item.getSubCategory()).orElse(Collections.emptyList())
.stream()
.map(...);
But it is different in the way, that requireNonNullElse() will directly return a non null List, while the Optional variant wraps the initial List in an Optional and is then unwrapped with orElse()
Is there a better way to handle null check to the list before
applying stream to it.
Only one way I can think of. Make item item.getSubCategory() to return Optional<YourObject> instead of the actual object.
I don't want to use IF statement to check null on getSubCategory. Is
there any better way in java steam API ?
Your concern doesn't have to do with stream API. What you ask is how to treat an object that could be null, so that I don't have to use If, else checks.
Perhaps this should be a good candidate for a comment, but I doubt one would be enough to explain my point.
I see very often people suggesting empty lists (or any empty collections) instead of null. In my view those are two completely different result types: empty list and absent list. Maybe sometimes an empty list is just fine, but that is not necessarily true in all the cases.
I agree that returning a null is a poor practice leading to lots of further issues. But null is not the only way of expressing absence of a result.
I'd rather use Optional.empty() instead of an empty list as a default result.
Here's an example demonstrating my point.
Let's assume we have a method fetching list of consumption records from an electricity meter. Sometimes the device might be offline, so you can't fetch anything and will retry later.
Now take a look at these two methods:
List<ConsumptionAmount> getConsumptionData(Date from, Date to);
Optional<List<ConsumptionAmount>> getConsumptionData(Date from, Date to);
The first implementation is very tricky. Returning empty list is very confusing (hence saying there was no energy consumption at all).
List<ConsumptionAmount> consumptionData = getConsumptionData(from, to);
// report 'zero consumption' to the billing service
Another option is to propagate an exception, which can also cause a try-catch mess in the code (The caller must be aware of the exception types and behave accordingly, otherwise the exception will be propagated further causing an even bigger mess):
try {
List<ConsumptionAmount> consumptionData = getConsumptionData(from, to);
// report consumption to the billing service
} catch (WhateverException ex) {
// handle missing data here
}
The second implementation in contrast does not provide any details on the issue causing the absence of data, but it doesn't confuse the caller neither.
getConsumptionData(from, to).ifPresent( list -> /* report consumption here */);
If you do care of missing result, then simply provide another method ref:
getConsumptionData(from, to).ifPresentOrElse(
list -> /* report consumption here */,
() -> /* handle missing data here */
);
There are even more interesting functional approaches to such use cases (Either and Try), but unfortunately Java doesn't provide them out of the box. You can check third party frameworks like this one
I'm occasionally getting a ConcurrentModificationException when I iterate over a list. A Google search informs me that it's probably because I'm altering that list in another thread while iterating over it and that to make this problem go away I should use java.util.concurrent.CopyOnWriteArrayList....
... except I already am.
Apparently, I'm doing something really stupid somewhere.
Does anybody have any insight into how one might induce CopyOnWriteArrayList to toss a ConcurrentModificationException? If it matters, I'm using Java 5.
Edit: Since the mutators I'm using may matter, I'm modifying this list in two ways:
Adding elements to the front. (list.add(0, newElement);)
Using subList to let older items fall off the back. (list = list.subList(0, MAX_LIST_SIZE);)
Do those raise red flags? If so, why? My understanding was that because these operations make a copy of the thing first, any existing iterators would be pointing at the unmodified original and would thus not care. Do I have a hole in my knowledge?
Edit 2: The precise code that's causing the problem is still a bit murky, but I can at least post the exception I'm seeing:
java.util.ConcurrentModificationException
at java.util.concurrent.CopyOnWriteArrayList$COWSubList.checkForComodification(Unknown Source)
at java.util.concurrent.CopyOnWriteArrayList$COWSubList.iterator(Unknown Source)
at....
... where it points to a for-each loop instantiation in my code.
That COWSubList does seem to imply that my call to subList is the root of my problem; I'd still like to understand why.
Edit 3: *facepalm*
CopyOnWriteArrayList.subList() returns a List, not a CopyOnWriteArrayList. The list it returns is under no implied obligation to provide any of COWAL's protections. Which makes using subList() like this to remove elements a Very Bad Idea.
Don't know for certain if this is my culprit, but it's damned suspicious and needs to be corrected regardless.
CopyOnWriteArrayList.subLists throw ConcurrentModificationExceptions if the containing list changes out from underneath it:
public class ListTest {
private static List<int[]> intList;
public static void main (String[] args) {
CopyOnWriteArrayList<Integer> cowal = new CopyOnWriteArrayList<Integer>();
cowal.add(1);
cowal.add(2);
cowal.add(3);
List<Integer> sub = cowal.subList(1, 2);
cowal.add(4);
sub.get(0); //throws ConcurrentModificationException
}
}
Sbodd has the correct answer, but it sounds like using CopyOnWriteArrayList instead of ArrayList is just an attempt to mask the error. The true problem is an attempt to modify the underlying list while iterating over it. You need to find where in your code you are accessing it as such and remove that usage or work around it.
For example, some method has the next implementation:
void setExcludedCategories(List<Long> excludedCategories) {
if (excludedCategories.contains(1L)) {
excludedCategories.remove(1L);
}
}
And it's called in the next way:
setExcludedCategories(Array.asList(1L, 2L, 3L));
Of course, it will lead ot an exception java.lang.UnsupportedOperationException when it will try to remove item.
The question: how can I modify this code to be sure that the input parameter excludedCategories supports remove?
UPD:
Thanks for answers. Let's summarize results:
Always create new ArrayList from the input list to be sure it's mutable - a lot of useless memory would be used -> NO.
Catch the UnsupportedOperationException.
Specify in the JavaDoc that a caller mustn't pass an immutable list - anybody read the JavaDoc? When something doesn't work only :)
Don't use Arrays.asList() in a caller's code - that's an option, if you an owner of this code, but anyway you should know if this concrete method allows immutable or not (see 3).
It seems the second variant is the only way to resolve this problem.
How can I modify this code to be sure that the input parameter excludedCategories supports remove?
In the general case, you can't. Given an arbitrary class that implements the List API, you cannot tell (statically or dynamically) if the optional methods are supported.
You can use instanceof tests to check if the class of the list is known to implement the method or to not implement it. For example ArrayList and LinkedList do, but Collections.UnmodifiableList does not. The problem is that your code could encounter list classes that your tests don't cover. (Especially if it is a library that is intended to be reusable in other peoples applications.)
You could also try to test the behavior of previously unknown classes; e.g. create a test instance, try a remove to see what happens, and record the behavior in a Map<Class, Boolean>. There are two problems with this:
You may not be able to (correctly) instantiate the list class to test it.
The behavior could depend on how you instantiate the class (e.g. constructor parameters) or even on the nature of the element you are trying to remove ... though the latter is pushing the boundary of plausibility.
In fact, the only completely reliable approach is to call the method and catch the exception (if it is thrown) each and every time.
In short, you can't know. If an object implements an interface (such as List) you can't know if it will actually do what is expected for all of the methods. For instance Collections.unmodifiableList() returns a List that throws UnsupportedOperationException. It can't be filtered out via the method signature if you want to be able to get other List implementations.
The best you can do is to throw IllegalArgumentException for known subtypes that don't support what you want. And catch UnsupportedOperationException for other types of cases. But really you should javadoc your method with what is required and that it throws IllegalArgumentException in other cases.
That depends somewhat on what you're trying to do. In your posted example for example you could just catch the UnsupportedOperationException and do something else instead.
This assumes that you can assume that non-mutable containers will throw that on every attempt to modify the container and will do so without side effects (that is they are indeed non-mutable).
In other cases where your code has other side effects than trying to modify the container you will have to make sure these doesn't happen before knowing that you can modify the container.
You can catch the exception in an utility class like in the example below (as others mentioned). Bad thing is you have to do insert/delete to test if there will be exception. You can not use instanceof since all Collections.Unmodifiablexxx classes have default access.
CollectionUtils:
import java.util.List;
public class CollectionUtils {
public <T> boolean isUnmodifiableList(List<T> listToCheck) {
T object = listToCheck.get(0);
try {
listToCheck.remove(object);
} catch (UnsupportedOperationException unsupportedOperationException) {
return true;
}
listToCheck.add(0, object);
return false;
}
}
Main:
import java.util.Arrays;
import java.util.List;
public class Main {
private static final CollectionUtils COLLECTION_UTILS = new CollectionUtils();
public static void main(String[] args) {
setExcludedCategories(Arrays.asList(1L, 2L, 3L));
}
private static void setExcludedCategories(List<Long> excludedCategories) {
if (excludedCategories.contains(1L)) {
if(!COLLECTION_UTILS.<Long>isUnmodifiableList(excludedCategories)){
excludedCategories.remove(1L);
}
}
}
}
Arrays.asList(T... a) returns the List<java.util.Arrays.ArrayList<E>> which is an immutable list. To get your code working just wrap the result with java.util.ArrayList<T> like shown below
setExcludedCategories(new ArrayList<Long>(Arrays.asList(1L, 2L, 3L)));
Always create new ArrayList from the input list to be sure it's mutable - a lot of useless memory would be used -> NO.
Thats actually the preferred way to do things. "A lot of useless memory" isn't a lot in most practical situations, certainly not in your cited exampled.
And ignoring that, its the only robust and inutitively understood idiom.
The only workable alternative would be to explicitly change the name of your method (thus communicating its behavior better), form the example you show, name it "removeExcludedCategories" if its meant to modify the argument list (but not an objects state).
Otherwise if it is meant as a bulk-setter, you're out of luck, there is no commonly recognized naming idiom that clearly communicates that the argument collection is directly incorporated into the state of an object (its dangerous also because the objects state can then be altered without the object knowing about it).
Also, only marginally related, I would design not an exclusion list, but an exclusion set. Sets are conceptually better suited (no duplicates) and there are set implementations that have far better runtime complexity for the most commonly asked question: contains().
if I want to call a method like this:
List f(List l){
l.add(new Object());
return l;
}
All is fine except if I call the method, it actually modifies its argument, is there anyway around that?
// suppose l is instantiated at this point
log.info(l.count());// prints 0
f(l);
log.info(l.count());// prints 1
is there anyway to declare f in a way to keep l unchanged in java?
I know that I can perform a deep clone on l and pass it, but in cases where l is really big, this operation is expensive.
Well, don't call a method which would modify it. What would you expect such a method to do, without copying? It would either have to behave differently (e.g. not doing anything when add is called) or throw an exception. You can make it throw an exception by wrapping it in an unmodifiable list... but if the purpose of the method is to change the collection, you probably don't want an exception to be thrown...
I know this sounds like a bit of a trite answer, but I hope it really gets to the heart of what you need to think about: if you have a collection which shouldn't be modified, and you want to call a method which tries to modify a collection, you should be considering why you want to call that method in the first place.
I do understand that the difficult part is knowing which methods will modify the collection - and that's where you can either defensively create an unmodifiable wrapper, or make sure all the relevant methods are documented correctly.
Use an unmodifiable list:
log.info(l.count());
f(Collections.unmodifiableList(list));
log.info(l.count());
If you try to modify the list within the method, you will get an UnsupportedOperationException.
If you don't want to change the original list, don't change it.
You can instead change a copy.
List f(List l){
l = new ArrayList(l); // the original will not be changed now.
l.add(new Object());
return l;
}
This is why you should always start with writing a specification and take good notice of the one provided to you about APIs. This would be listed in the specification of the method.
If you want to force that no changes are made to your list, disregarding that the spec says it will attempt to do so (assuming you did not write the method itself), wrap it up with as an Collections.unmodifiableList(l); and handle the thrown exceptions, as the others suggested.
If you are on the other side - writing the method and you want to make sure you don't change the contents of the list - just don't write any modifying statements and make sure to mention that in the spec.
If you know that your original list does not change by itself, and want a new List which contains all contents of the original list and additionally your one new element, you could consider using a wrapper around both, like this:
/**
* an immutable wrapper around a list with an added element at the end.
*/
class ImmutableListWrapper<E> extends AbstractList<E> {
private final List<E> delegate;
private final E lastElement;
public ImmutableListWrapper(List<E> start, E last) {
this.delegate = start;
this.lastElement = last;
}
public E get(int index) {
if(index == delegate.size()) {
return lastElement;
}
return delegate.get(index);
}
public int size() {
return delegate.size() + 1;
}
}
public List<Object> f(List<Object> l) {
return new ImmutableListWrapper<Object>(l, new Object());
}
If the original list changes, the new list changes too, this is by design.
If your original list is a no-random-access list, then you would better inherit from AbstractSequentialList and implement a delegating ListIterator instead of a get-method.
I'm occasionally getting a ConcurrentModificationException when I iterate over a list. A Google search informs me that it's probably because I'm altering that list in another thread while iterating over it and that to make this problem go away I should use java.util.concurrent.CopyOnWriteArrayList....
... except I already am.
Apparently, I'm doing something really stupid somewhere.
Does anybody have any insight into how one might induce CopyOnWriteArrayList to toss a ConcurrentModificationException? If it matters, I'm using Java 5.
Edit: Since the mutators I'm using may matter, I'm modifying this list in two ways:
Adding elements to the front. (list.add(0, newElement);)
Using subList to let older items fall off the back. (list = list.subList(0, MAX_LIST_SIZE);)
Do those raise red flags? If so, why? My understanding was that because these operations make a copy of the thing first, any existing iterators would be pointing at the unmodified original and would thus not care. Do I have a hole in my knowledge?
Edit 2: The precise code that's causing the problem is still a bit murky, but I can at least post the exception I'm seeing:
java.util.ConcurrentModificationException
at java.util.concurrent.CopyOnWriteArrayList$COWSubList.checkForComodification(Unknown Source)
at java.util.concurrent.CopyOnWriteArrayList$COWSubList.iterator(Unknown Source)
at....
... where it points to a for-each loop instantiation in my code.
That COWSubList does seem to imply that my call to subList is the root of my problem; I'd still like to understand why.
Edit 3: *facepalm*
CopyOnWriteArrayList.subList() returns a List, not a CopyOnWriteArrayList. The list it returns is under no implied obligation to provide any of COWAL's protections. Which makes using subList() like this to remove elements a Very Bad Idea.
Don't know for certain if this is my culprit, but it's damned suspicious and needs to be corrected regardless.
CopyOnWriteArrayList.subLists throw ConcurrentModificationExceptions if the containing list changes out from underneath it:
public class ListTest {
private static List<int[]> intList;
public static void main (String[] args) {
CopyOnWriteArrayList<Integer> cowal = new CopyOnWriteArrayList<Integer>();
cowal.add(1);
cowal.add(2);
cowal.add(3);
List<Integer> sub = cowal.subList(1, 2);
cowal.add(4);
sub.get(0); //throws ConcurrentModificationException
}
}
Sbodd has the correct answer, but it sounds like using CopyOnWriteArrayList instead of ArrayList is just an attempt to mask the error. The true problem is an attempt to modify the underlying list while iterating over it. You need to find where in your code you are accessing it as such and remove that usage or work around it.