I have objects Bullet that I add to two ArrayLists at once, the lists are briefly described below. After certain operations are done I wish to remove a bullet from both lists. Is this approach correct? I keep on getting an error: java.util.ConcurrentModificationException
Alternatively, can you think of a better solution than ArrayList for the purpose of handling objects in this manner?
//there are ArrayList<Bullet> bullets and ArrayList<Updatable> updatable, in the class
public void removeBullet(Bullet bullet) {
for (ListIterator<Bullet> bulletIterator = bullets.listIterator(); bulletIterator.hasNext();) {
Bullet tempBullet = bulletIterator.next();
if (tempBullet.equals(bullet)) {
for (ListIterator<Updatable> updatableIterator = updatable.listIterator(); updatableIterator.hasNext();) {
Updatable tempUpdatable = updatableIterator.next();
if (tempUpdatable.equals(bullet)) {
updatableIterator.remove();
bulletIterator.remove();
return;
}
}
}
}
}
EDIT: The source of problem was that I used an iterator on one of the lists, at exact same time in a different place, hence the error. This code worked fine for the updatable list.
A ConcurrentModificationException happens because you are trying to remove a bullet from an Iterator which you are also simultaneously iterating through in a for loop; java doesn't like when you do that and will throw the exception.
To solve this, you would have to iterate through both Iterators and remove them separately, or, as rdonuk stated, simply use the ArrayList remove() method, which will not throw any exceptions if you try to remove something that isn't in the ArrayList; it will return true if the object was removed, or false otherwise, so you don't even have to check if the object you want to remove is contained in the ArrayList in the first place.
Just use ArrayList remove method.
bullets.remove(bullet);
and
updatable.remove(bullet);
Edit:
remove method of iterator which used by ArrayList:
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
As you see it is already use ArrayList.remove() method.
Related
In the Head First Design Patterns book, the authors describe using an iterator to traverse over composite data structures. They provide some sample code which, when executed, prints out a series of menu items stored within the composite. However, if you try to call the iterator more than once, it no longer works as expected and won't produce any results. The following code appears to be causing the problem:
public Iterator<MenuComponent> createIterator() {
if (iterator == null) {
iterator = new CompositeIterator(menuComponents.iterator());
}
return iterator;
}
In essence, they are creating a singleton iterator that cannot be reset for future iterations. Unfortunately, simply replacing this logic to return a new instance of the CompositeIterator also breaks the algorithm. An issue was raised on GitHub several years ago, although is yet to be resolved. Does anyone have any suggestions on how to overcome this issue?
As the linked issue says in the comments:
return iterator; // the `iterator' never resets to null once it's set.
We need to reset the iterator we are done with it, but not when the iterator still has elements left, because CompositeIterator depends on that.
One way to do this is to add another condition on which iterator is reset - when the iterator has no more elements:
public Iterator<MenuComponent> createIterator() {
if (iterator == null || !iterator.hasNext()) {
iterator = new CompositeIterator(menuComponents.iterator());
}
return iterator;
}
Consider the following code snippet:
private List<Listener<E>> listenerList = new CopyOnWriteArrayList<Listener<E>>();
public void addListener(Listener<E> listener) {
if (listener != null) {
listenerList.add(listener);
}
}
public void removeListener(Listener<E> listener) {
if (listener != null) {
listenerList.remove(listener);
}
}
protected final void fireChangedForward(Event<E> event) {
for (Listener<E> listener : listenerList) {
listener.changed(event);
}
}
protected final void fireChangedReversed(Event<E> event) {
final ListIterator<Listener<E>> li = listenerList.listIterator(listenerList.size());
while (li.hasPrevious()) {
li.previous().changed(event);
}
}
There is a listener list that can be modified and iterated.
I think the forward iteration (see method #fireChangedForward)
should be safe.
The question is: is the reverse iteration (see method #fireChangedReversed) also safe in a multi-threaded environment?
I doubt that, because there are two calls involved: #size and #listIterator.
If it's not thread-safe, what is the most efficient way to implement #fireChangedReversed under the following circumstances:
optimize for traversal
avoid usage of locking if possible
avoid usage of javax.swing.event.EventListenerList
prefer solution without usage of third-party lib, e.g. implementation in own code possible
Indeed, listenerList.listIterator(listenerList.size()) is not thread-safe, for exactly the reason you suggested: the list could change size between the calls to size() and listIterator(), resulting in either the omission of an element from the iteration, or IndexOutOfBoundsException being thrown.
The best way to deal with this is to clone the CopyOnWriteArrayList before getting the iterator:
CopyOnWriteArrayList<Listener<E>> listenerList = ... ;
#SuppressWarnings("unchecked")
List<Listener<E>> copy = (List<Listener<E>>)listenerList.clone();
ListIterator<Listener<E>> li = copy.listIterator(copy.size());
The clone makes a shallow copy of the list. In particular, the clone shares the internal array with the original. This isn't entirely obvious from the specification, which says merely
Returns a shallow copy of this list. (The elements themselves are not copied.)
(When I read this, I thought "Of course the elements aren't copied; this is a shallow copy!" What this really means is that neither the elements nor the array that contains them are copied.)
This is fairly inconvenient, including the lack of a covariant override of clone(), requiring an unchecked cast.
Some potential enhancements are discussed in JDK-6821196 and JDK-8149509. The former bug also links to a discussion of this issue on the concurrency-interest mailing list.
One simple way to do that is to call #toArray method and iterate over the array in reverse order.
You could always just get a ListIterator and "fast-forward" to the end of the list as such:
final ListIterator<Listener<E>> li = listenerList.listIterator();
if (li.hasNext()) {
do{
li.next();
} while (li.hasNext());
}
while (li.hasPrevious()) {
li.previous().changed(event);
}
EDIT I switched the quirky exception-handling of my previous answer for a do/while loop that places the cursor of the ListIterator after the last element, in order to be ready for the next previous call.
RE-EDIT As pointed out by #MikeFHay, a do/while loop on an iterator will throw a NoSuchElementException on an empty list. To prevent this from happening, I wrapped the do/while loop with if (li.hasNext()).
Given this method:
public void walk( String path , ArrayList<String> files, String ext)
which collects all files into the ArrayList<> files starting at path and with given extension ext, I'm looking for a way to stop the search when a certain condition is met. For example, it should stop when files.size() becomes greater than a given number. How could I do this without modifying the method walk() ?
By not modifying the method, I mean not touching the source code in the editor. It's in a state that I like, and I don't want to touch it, because it's just for testing purpose.
Create your class extending ArrayList and override add method:
public class MyList extends ArrayList<String> {
#Override
public boolean add(String item) {
boolean added = super.add(item);
if (added && size() >= 10) {
throw MaxItemsReachedException();
}
}
}
When size is greater or equals to 10, for instance, you can throw an exception.
And call your method with an instance of MyList instead of ArrayList:
MyList list = new MyList();
walk("path", list, "extension");
DISCLAIMER: This is bad programming practice. Dont't do this. I only offer it because it solves the OP's problem.
Subclass ArrayList. Add some logic to the add methods that throw an exception if files.size is greater than some threshold.
It will look like this
public void add(E element){
if(size()<THRESHOLD){
super.add(element);
}else{
throw new RuntimeException("STOP HERE");
}
}
Try to throw an exception that walk does not catch and you should catch this exception in the method that calls walk.
Among other bad things this is using exceptions to manage flow control.
We all know that the safest "and probably only safe" way of removing an object from a collection while iterating it, is by first retrieving the Iterator, perform a loop and remove when needed;
Iterator iter=Collection.iterator();
while(iter.hasNext()){
Object o=iter.next()
if(o.equals(what i'm looking for)){
iter.remove();
}
}
What I would like to understand, and unfortunately haven't found a deep technical explanation about, is how this removal is performed,
If:
for(Object o:myCollection().getObjects()){
if(o.equals(what i'm looking for)){
myCollection.remove(o);
}
}
Will throw a ConcurrentModificationException, what does "in technical terms" Iterator.remove() do? Does it removes the object, breaks the loop and restart the loop?
I see in the official documentation:
"Removes the current element. Throws IllegalStateException if an
attempt is made to call remove() that is not preceded by a call to
next( )."
The part "removes the current element", makes me think of the exact same situation happening in a "regular" loop => (perform equality test and remove if needed), but why is the Iterator loop ConcurrentModification-safe?
The reason why you cannot modify a list while iterating over it is because the iterator has to know what to return for hasNext() and next().
How this is done is implementation specific, but you could have a look at the source code of ArrayList/AbstractList/LinkedList etc.
Also note that in some situations you can use some code like this as an alternative:
List<Foo> copyList = new ArrayList<>(origList);
for (Foo foo : copyList){
if (condition){
origList.remove(foo);
}
}
But this code will probably run slightly slower because the collection has to be copied (shallow copy only) and the element to remove has to be searched.
Also note that if you're using the iterator directly it's recommended to use a for loop instead of while loop as this limits the scope of the variable:
for (Iterator<Foo> iterator = myCollection.iterator(); iterator.hasNext();){
...
}
How exactly Iterator removes elements depends on its implementation, which may be different for different Collections. Definitely it doesn't break the loop you're in. I've just looked how ArrayList iterator is implemented and here's the code:
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
So it checks for concurrent modifications, removes element using public ArrayList remove method, and increments counter of list modifications so ConcurrentModificationException won't be thrown at next iteration.
public void searchOwner(List<Appointments> appts, String owner) {
Appointments theOne = null;
for (Appointments temp : appts) {
if (owner.equalsIgnoreCase(temp.owner.name)) {
System.out.println(temp.data);
temp.setResolved(true);
}
}
}
public void checkRemoval() {
for (Appointments appts : appointments) {
if (appts.resolved == true) {
appointments.remove(appts);
}
//Iterator method used before enhanced for-loop
public void checkRemovalI(){
Iterator<Appointments> it = appointments.iterator();
while(it.hasNext()){
if(it.next().resolved = true){
it.remove();
}
}
}
So far this is where I am encountering my problem. I am trying to check the arrayList of Appointments and see if the field (resolved) is set to true, however I am receiving an ConcurrentModification exception during the searchOwner method when trying to set resolved = to true. I've tried using an Iterator in checkRemoval instead of an enhanced for-loop however that didn't help either. I really only need to get the part where the appointment is set to true to work, the checkRemoval seemed to be working early before implementing the changing of the boolean resolved. Any help will be greatly appreciated, thank you.
I'm willing to bet that the ConcurrentModificationException is not being caused where you say it is, but rather in checkRemoval(), which you're probably calling before the line you mention where you set resolved to true, hence your confusion.
I only say this because:
for (Appointments appts : appointments) {
if (appts.resolved == true) {
appointments.remove(appts);
}
}
is a blatant concurrent modification. You cannot remove elements from a collection while you are iterating through it in a loop. Instead, you need to use an iterator:
public void checkRemoval() {
Iterator<Appointment> apptsIterator = appointments.iterator();
while (apptsIterator.hasNext()){
if (appts.next().resolved == true)
apptsIterator.remove(); //removes the last element you got via next()
}
The ConcurrentModification exception is thrown, using the for loop, where the Collection gets modified. So the issue need not be the code that you haave posted. You might be having a loop over the appts List, which is calling this function.
Posting more of your code might help.