views:

7652

answers:

7

Hi!

This question is a more special case of the problem described (and solved) in this question.

I have two methods, stopAndRemove(ServerObject server) and a close() method. The later should close all servers and remove them from the server list. The list is defined as

List<ServerObject> server.

I do not want to have almost the same code from stopAndRemove in closeCurrentlyOpen, so I want to do something like:

public void closeCurrentlyOpen() {
   for(ServerObject server : this.servers) {
       stopAndRemove(server)
   }
}

This won't work, as this will cause a ConcurrentModificationException. I tried to make a copy of the list

List<ServerObject> copyList = new ArrayList<ServerObject>(this.servers);

and use that as the list for the foreach-loop. But then it might be possible that an other thread appends a Server to the servers list while I am iterating over copyList but closeCurrentlyOpen is supposed to result in an emtpy list. As the addServerToList method is synchronized to the servers-list, doing this

public void closeCurrentlyOpen() {
   synchronized(this.servers) {
     for(ServerObject server : this.servers) {
        stopAndRemove(server)
     }
    }
}

will solve the problem with modifications. But then I can not synchronize the code in the stopAndRemove method which is necessary if it is directly called.

I seems to me that the design of this three methods probably needs a workover. Ideas anybody?

+1  A: 

When I've done this before, I always used the "old school" LinkedList collection, an Iterator, and the Iterator.remove() method to remove the current item.

Eric Petroelje
+2  A: 

Refactor out all the ServerObject stopping code from stopAndRemove into a private stopServer method, and then do the removal separately in stopAndRemove and closeCurrentlyOpen. Then you can use a ListIterator to remove them (or just stop them all in a for loop and clear the list at the end).

Pesto
this is the way i do it now. I think that is also what starblue suggested.
ManuelFuenfrocken
+7  A: 

Split off a method stop() from stopAndRemove(). Then write the loop with an explicit iterator, do the stop and then iterator.remove().

"and" in a method name is a code smell.

starblue
I've done so... stopping all elements from the stop-loop, then list.clean() to remove all. Yes, "and" is code smell here. I gave it this name in the example so I do not need to describe what i does.
ManuelFuenfrocken
A: 

You should get an iterator and remove using it. You are getting the exception because iterators are fail-fast in java.

I'm not the down-voter but: it's not iterator that are fail-fast but the implementation of the list provided in the jdk.
Nicolas
Maybe I should've said java collection use fail fast iterators.
+1  A: 

You might find this article about ConcurrentModificationException has some advice in this area.

Alex Miller
+2  A: 

Perhaps this is the wrong way to do it, but I always create a removal collection, which contains indexes or references to the objects that need to be removed. I then iterate over that collection and remove those indexes/objects from the original collection. Probably not the most efficient but it got the job done.

Instead of

for(Collection things : thing)  
    things.remove(thing)

I use

Collection toRemove = new LinkedList();
for(things : thing)
    toRemove.add(thing);

for(toRemove : thing)
    things.remove(thing)
firebird84
No, that is the right way to do it. Or create a new collection without the removed items and replace the original collection with it. Removing items from a collection while you're iterating through is the kind of shit that gives imperetive code a bad name :)
U62
imperative*Sorry, I can't help it :(
firebird84
The problem I see here is: This solution want stop any thread from adding data to the list. In the other solutions above this is done through synchronzied-blocks.
ManuelFuenfrocken
You can still wrap the whole thing with synchronized{} if you are indeed in a multithreaded environment. Java will throw a concurrency exception in the Q's example because you are modifying the data store while you have an iterator open on it. In this situation, that does not happen.
firebird84
For clarification on my previous comment: In my example, the concurrency exception should not occur.
firebird84
+1  A: 

Answering to the title of the question, not the specific details of the given example. In fact, this solution is not even appropriate in the given situation (refactoring is appropriate, as suggested by others).

However, it seems that many java programmers are not aware of CopyOnWriteArrayList (part of JDK since 1.5) and are trying to roll their own solutions to the same problem (copy list before iterating).

Neeme Praks