I have the following code:
Map<String, List<String>> map;
for(String k : map.keySet()){
List<String> list = map.get(k);
boolean empty = list.isEmpty();//CME
if(!empty && somecheck(k, ...)){
list.clear();
}
}
And I'm getting ConcurrentModificationException in isEmpty() method. List is an ArrayList. There is no other threads modifying list, because it was created in this method before (and the all map too).
The only place modifying list is clear(), but it called after isEmpty() and loop cannot execute on one list twice.
I'm using java 1.7
java.util.ConcurrentModificationException
at java.util.ArrayList$SubList.checkForComodification(ArrayList.java:1169)
at java.util.ArrayList$SubList.size(ArrayList.java:998)
at java.util.AbstractCollection.isEmpty(AbstractCollection.java:86)
From the stacktrace you have given it appears the exception is being thrown in the sub-class implementing the SubList functionality - I assume the lists in your map are actually sublists of another list?
Presumably what is happening is that you are modifying the underlying list after you have created the sublist view (remember, a sublist is just a view onto another list - it does not take an independent copy).
Instead of putting sublists into the map, try taking a copy instead, for example:
map.put(key, new ArrayList(originalList.subList(start, end));
Related
I have a code that adds data to a list. What I do not understand is why
the UnsupportedOperationException is thrown in one case and
ConcurrentModificationException in the other.
I am adding data in list in both the case and then trying to remove list
data while iterating over the list.
What i have read so far is that whenever any modification is made to
fail- fast collection,ConcurrentModificationException is thrown. So why this
different behavior in both these cases?
List<String> animalList = new ArrayList<>();
animalList.add("cat");
animalList.add("dog");
animalList.add("bear");
animalList.add("lion");
Iterator<String> iter = animalList.iterator();
while(iter.hasNext()){
String animal = iter.next();
System.out.println(animal);
animalList.remove(3);
}
This code throws ConcurrentModificationException
String[] strings = { "Java", "Honk", "Test" };
List<String> list = Arrays.asList(strings);
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
String name = iterator.next();
System.out.println(name);
list.remove(3);
}
while this one throws UnsupportedOperationException
For the code block, where you get ConcurrentModificationException , you get that exception because you created an iterator on List then removing directly from list from within loop so iterator has issues. You should use remove() method of iterator itself - Iterator.remove().
You should directly remove an element from list when removing from outside iterator loop. See this another SO Question
In second case, with Arrays.asList , you get a List but actual list object might not be an ArrayList and remove(int index) operation is optional at List interface. See this
All in all, as far as UnsupportedOperationException is concerned, in first case you are guaranteed to working with an ArrayList and for that class, remove operation is supported , See this
For second case, you can't be so sure. Refer documentation of Arrays.asList where it says that returned list of fixed size so certain operations are not supported.
Arrays.asList does not return an ArrayList. In fact, the list returned is not modifiable, thus when you try to modify it, it throws the UnsupportedOperationException.
The following code works fine when there are more than one modification in a particular map. But when there is only one modification it throws concurrent modification exception
for(Map.Entry<String, List<String>> mapEntry : beanMap.entrySet()) {
for(String dateSet : dateList) {
String mName = mapEntry.getKey();
boolean dateFound = false;
if(beanMap.containsKey(dateSet)) {
dateFound = true;
System.out.println(" Found : "+mapEntry.getKey());
}
if(!dateFound)
{
Map<String, List<String>> modifiedMap = beanMap;
List<String> newBeanList = new ArrayList<String>();
dBean beanData = new Bean(dateSet+"NA","NA","NA",0,0,0);
newBeanList.add(beanData);
System.out.println(" Adding : "+dateSet+" "+"NA");
modifiedMap.put(mName, newBeanList);
}
}
}
In the above code it throws ConcurrentModificationException when modifying the "modifiedMap" only once. May be there is more to it but couldn't find out why.
When you use an enhanced for loop, there is an implicit Iterator working behind the scenes. You attempt to make a copy of beanMap with this line:
Map<String, List<String>> modifiedMap = beanMap;
However, this only creates another reference variable that also refers to the same map object. There is still only one map, and you are modifying it:
modifiedMap.put(mName, newBeanList);
The Iterator then detects that the map is modified when it attempts to iterate to the next entry, resulting in the ConcurrentModificationException.
You can create another Map with new, and put all your modifications into that map while you're iterating the original map.
After you're done iterating the original map, you can call the putAll method on it, passing your new map, to apply all of the modifications you want.
You are not allowed to change the underlying collection while iterating over it using this syntax. The collections are implemented in a fail-fast way, so even a single change will raise the exception.
If you need to change the collection while visiting the elements, use an Iterator.
modifiedMap is the reference to same Map beanMap on which you are iterating. You are modifying the Collection modifiedMap while iteration hence the Exception.
I understand that this exception is occurring because I'm trying to modify a list while I'm iterating through a list, but I don't understand how to get around it.
for(Villager e : tasked)
{
if(!e.hasTask())
{
tasked.remove(e);
}
}
The code is pretty self-explanatory. If the villager "e" does not have a task, it should be removed from this list.
Use a ListIterator, which allows you to make changes to the list through which you are iterating.
The code you use is almost indentical in use to an iterator, but removing elements has to be done explicitly using the remove() method of the iterator.
e.g.
Iterator itr = tasked.iterator();
while(itr.hasNext()) {
Villager e = itr.next();
if(!e.hasTask()) {
itr.remove();
}
}
The ConcurrentModificationException is a RuntimeException that may be thrown by methods that have detected concurrent modification of an object, when such modification is not permissible. An example of not permissible behavior is when a thread tries to modify the internal structure of a Collection, while another thread is iterating over it.
Use Iterator's remove method.
Iterator<Village> villageItr = tasked.iterator();
while(villageItr.hasNext()){
Village e=villageItr.next();
if(!e.hasTask()){
//remove that element from collection
villageItr.remove();
}
}
Create separate list (e.g. itemsToRemove), that contains all items you want to remove and then use
tasked.removeAll(itemsToRemoveList)
e.g.
List<Villager> toRemove = new ArrayList();
for(Villager e : tasked)
{
if(!e.hasTask())
{
toRemove.add(e);
}
}
tasked.removeAll(toRemove);
The Only issue with this approach, if the 'toRemove' list size is very large, you can do the opposite 'ls.retainAll(c)', by only identify what you want to keep.
If you are using java8 you can filter the list using stream API instead of iterating over it by hand.
List<Villager> villagersWithTask = tasked.stream().filter(e -> e.hasTask()).collect(Collectors.toList());
I currently have a problem with iterating through an ArrayList. I've read several posts here, but nothing seem to have resolved my problem. Here is my code:
//restaurants contains a list of all restaurants and i want to filter them
List<Restaurant> newList = new ArrayList<Restaurant>();
List<Restaurant> allRestaurants = new ArrayList<Restaurant>(restaurants);
if (query != null && query.length() > 0 && !query.equals("*")) {
synchronized (allRestaurants) {
for (Iterator<Restaurant> it = allRestaurants.iterator(); it
.hasNext();) {
Restaurant restaurant = it.next();
if (restaurant.getCity().contains(query)) {
synchronized (newList) {
newList.add(restaurant);
}
} else {
newList = allRestaurants;
}
}
}
This is code was modified by me with several ideas i've read here (synchronized, using iterator instead of for-each-loop). I even have synchronized the whole method and still get an exception.
The exception is happening in following line:
Restaurant restaurant = it.next();
which I don't understand. I am not manipulating the list in this line. Why is this happening and how can i fix it?
else{
newList = allRestaurants;
}
That is almost certainly your issue.
Assigning newList to allRestaurants then adding to newList is causing your comodification.
That is after newList = allRestaurants any add to newList will update the mod count in allRestaurants and thus your error.
In the else branch
else {
newList = allRestaurants;
}
You set newList to be allRestaurants. The next modification newList.add(restaurant); changes the allRestaurants-list.
The exception is thrown when it.next() is called, because then the iterator checks if its source was changed.
The failure starts with:
newList = allRestaurants;
which points both references to the same list (i.e. the one you are iterating over). You then do the following:
newList.add(restaurant);
modifying the list. From the javadoc of ConcurrentModificationException:
Note that this exception does not always indicate that an object has been concurrently modified by a different thread. If a single thread issues a sequence of method invocations that violates the contract of an object, the object may throw this exception. For example, if a thread modifies a collection directly while it is iterating over the collection with a fail-fast iterator, the iterator will throw this exception.
Your problem is in the else clause.
newList = allRestaurants;
That's why you get exceptions
You can't change the ArrayList that is used for iteration inside a loop; that is what ConcurrentModificationException says (http://docs.oracle.com/javase/1.4.2/docs/api/java/util/ConcurrentModificationException.html) and newList = allRestaurants; plus newList.add(restaurant);does potentially change the list allRestaurants.
So what you could do is
create another list
put items to modify in that list
add/remove the new list (addAll or removeAll) to your old one after the loop
Check out http://www.javacodegeeks.com/2011/05/avoid-concurrentmodificationexception.html for more.
For some reason, I'm getting an UnsupportedOpeationException with the following code. Examining it in the debugger, it looks like the object I'm calling remove() on is a list.
// to optimize, remove totalSize. After taking an item from lowest, if lowest is empty, remove it from `lists`
// lists are sorted to begin with
public static <T extends Comparable<? super T>> List<T> merge(Set<List<T>> lists) {
List<T> result = new ArrayList<T>();
HashMap<List<T>, Integer> location = new HashMap<List<T>, Integer>();
int totalSize = 0; // every element in the set
for (List<T> l : lists) {
location.put(l, 0);
totalSize += l.size();
}
boolean first;
List<T> lowest = lists.iterator().next(); // the list with the lowest item to add
int index;
while (result.size() < totalSize) { // while we still have something to add
first = true;
for (List<T> l : lists) {
if (! l.isEmpty()) {
if (first) {
lowest = l;
}
else if (l.get(location.get(l)).compareTo(lowest.get(location.get(lowest))) <= 0) {
lowest = l;
}
}
}
index = location.get(lowest);
result.add(lowest.get(index));
lowest.remove(index); //problem here
}
return result;
}
The exception:
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.AbstractList.remove(Unknown Source)
at interview.questions.MergeLists.merge(MergeLists.java:72)
at interview.questions.MergeLists.main(MergeLists.java:32)
Why is this happening?
It's quite possible the underlying implementation of List you received is fixed-length, such as one created by Arrays#asList.
If you look at the API docs for the List interface you will see that a number of them are "optional operations". That means that a concrete class is permitted to throw the UnsupportedOperationException.
If, for example, the List was converted to an unmodifiable list it could not allow the remove operation to actually remove something (or the list would be modified).
So for the Set< List<>> part of the code one or mnre of the lists does not allow you to remove from it.
If you are going to be removing items from a List, then rather than use a for-each loop to iterate through the list, you should be using a ListIterator, which supports remove() in a safe manner (i.e. without leaving holes in the list or an index pointing to nowhere).
It is optional for a class implementing the Collection interface to allow objects to be removed (see Collection#remove() which is an optional operation). As stated in the javadoc, it throws
UnsupportedOperationException - if the remove operation is not supported by this collection
You are likely in that case (e.g. if your set contains a list returned by Arrays.asList as pointed out by Jeffrey).
Could it be that the Lists you pass in the set are derived from AbstractList and do not implement (support) the remove() method?
Also, it seems that location always maps to 0 for all list object that are mapped in the location HashMap?
The remove() method in the Collection interface is explicitly specified as an optional operation:
remove(Object o)
Removes a single instance of the specified element from this collection, if it is present (optional operation).
Lists do not have to support in. In fact, there is no clear semantics for it. Lists are not meant for that sort of random-access. Rather than provide some default implementation that may be inefficient or inaccurate, you get the exception.
You can write your own utility method with a for-each loop to do this if it is critical.