I am new to Java and especially to iterating through lists and modifying it's elements. I went through dozens of similar questions, but after many tries nothing worked.
In the following code sample an exceptions is thrown. It is not related to concurrent thread as I only have one thread. Netbeans output sais that the exception occurs in line 5(CustomerData customer = i.next();) :
CustomerData tempCustomer = new CustomerData("",tagID,0);
tempCustomer.setName(name);
tempCustomer.setSize(size);
for(ListIterator<CustomerData> i = customers.listIterator(); i.hasNext(); )
{
CustomerData customer = i.next();
if(customer.getTagID().contains(tagID))
{
Object[] options = {"overwrite", "do not overwrite"};
int n = JOptionPane.showOptionDialog(rootPane,
"TagID already exists. overwrite?",
"?",
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE,
null,
options,
rootPane);
if ( n == JOptionPane.YES_OPTION ){
i.set(tempCustomer);
}
}
else{
addCustomer();
}
}
The whole code is only supposed to check if the element has matching tagID and if it does, replace two values (name and size) in it. Initially I tried to use setName() and setSize() inside the for loop on the element, but after it didn't work and after reading other related questions, I assigned the values to temporary object before the loop and the used the iterator's set method to replace current element. But still no success and it even seems it never gets that far because the exception comes after line 5...
The problem is presumably that this line:
addCustomer();
is trying to modify customers while you're in the middle of iterating over it. This isn't allowed.
And anyway, it seems like a logic error: presumably you want to call addCustomer only once, after the loop, if no customer had the right tagID, but your current code tries to call addCustomer for each customer with with the wrong tagID. So you need to use a boolean variable during the loop to keep track of whether any customer matched, and then, after the loop, call addCustomer if appropriate.
ConcurentModificationException does not always indicate that an object has been concurrently modified by a different thread.
In your case the problem might be in following (guessing by addCustomer method): you are getting iterator of collection and then modifiying this collection. Because collection is modified, iterator becomes invalid and exception is thrown.
My suggestion will be create temproray list of customers which will you will append to your original customer list, when your iteration finished.
As mentioned in javadocs:
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.
I guess it is occurring after a call to addCustomer() which modifies the customers object. You can either hold the newly added objects in a different collection until end of the loop or break out of the loop and restart after adding.
Related
I'm working on some code and during testing it started throwing a ConcurrentModificationException. After some research, what confuses me is that I'm not adding or removing from the list while iterating. I do iterate over the list, but it's after I've added to the list. To add clarity, below is a commented version of the code:
File f = fc.getSelectedFile();
ConfigurationManager manager = new ConfigurationManager(f);
//The next line uses the same thread to get all Host objects from the file
ArrayList<Host> configHosts = manager.getAllHosts();
//Add the found hosts to a (previously established) ArrayList
hosts.addAll(configHosts);
for(Host h : hosts) { //Exception thrown points to this line.
//This reads values from the Host object and puts them in a JTable.
//No modification is done to the object, it is just read from.
this.updateTable(h);
}
I'm not sure how this code could possibly throw the ConcurrentModificationException, unless addAll() operates in a different thread (and then updateTable() would be called as hosts were still being added). But saving that, I'm not sure what I'm doing that's inappropriate.
I found the bug in my code. The commentators were right; updateTable() was (indirectly) responsible. There's a special case where certain hosts will be removed from the global field, and my test data just had to hit this special case. I'm not deleting the question because I believe there's value in my specific error. In short, if you get a ConcurrentModificationException, check the following:
Are you trying to remove/add an item from an ArrayList while iterating over the list?
Are you sure code that is reading the ArrayList side effect free (i.e. it won't modify the list under any case) (this was my problem)?
Are you working in a multithreaded environment where this ArrayList could be modified during your iteration (if so, you might want to synchronize the loop)?
I have a ConcurrentSKipListSet, and I'm iterating over values in this set with a for-each loop.
Another thread at some point is going to remove an element from this set.
I think I'm running into a situation where one thread removes an element that I'm yet to iterate over (or maybe I've just started to iterate over it) and so a call being made from within the loop fails.
Some code for clarity:
for(Foo foo : fooSet) {
//do stuff
//At this point in time, another thread removes this element from the set
//do some more stuff
callService(foo.getId()); // Fails
}
Reading the docs I can't work out if this is possible or not:
Iterators are weakly consistent, returning elements reflecting the state of the set at some point at or since the creation of the iterator. They do not throw ConcurrentModificationException, and may proceed concurrently with other operations.
So is this possible, and if so, what's a good way of handling this?
Thanks
Will
I think I'm running into a situation where one thread removes an element that I'm yet to iterate over (or maybe I've just started to iterate over it) and so a call being made from within the loop fails.
I don't think that's what the javadocs are saying:
Iterators are weakly consistent, returning elements reflecting the state of the set at some point at or since the creation of the iterator. They do not throw ConcurrentModificationException, and may proceed concurrently with other operations.
This is saying that you don't have to worry about someone removing from the ConcurrentSkipListSet at the same time that you are iterating across the list. There certainly is going to be a race condition as you are moving across the iterator however. Either foo gets removed right after your iterator gets it or it was removed right before and the iterator doesn't see it.
callService(foo.getId()); // this shouldn't "fail"
If foo gets returned by the iterator, your service call won't "fail" unless it is assuming that the foo is still in the list and somehow checking it. The worst case is that you might do some operations on foo and call the service with it even though it was just removed from the list by the other thread.
I've hit this problem as well with queues that are written to and read by different threads. One approach is to mark instead of remove elements that are no longer needed. You can run a cleanup iterator after you go through the whole list. You need a global lock just for removing elements from the list, and the rest of the time your code can run in parallel. Schematically it works like this:
writer:
while() {
set.add(something);
something.markForDelete();
}
reader:
while() {
// process async
iterator iter = set.getIterator();
for(iter.hasNext()) {
... work, check isMarkedForDelete() ...
}
iter = set.getIterator();
// delete, sync
globalLock.Lock();
for(iter.hasNext()) {
if(something.isMarkedForDelete()) {
set.remove(something);
}
globalLock.Unlock();
}
}
Please help me explaning this. I can't seem to figure out why this produce a null pointer exception
/* Try to find customer in customer list, if not in list add to list with a
new plan; otherwise add additional plan to customer*/
for(int a = 0; a < dataset.length; a++){
if(customer_name_list.contains(dataset[a][CLIENT_NAME])){
int temp_index = 0;
//NULLPOINTEREXCEPTION OCCURRED ON THE FOLLOWING LINE
while(!customer.get(temp_index).name.equals(dataset[a][CLIENT_NAME])){
temp_index++;
}
customer.get(temp_index).add_plan(dataset[a][PLAN], dataset[a][DETAIL]);
}
else{
Customer temp_customer = new Customer(dataset[a][CLIENT_NAME], dataset[a][PLAN], dataset[a][DETAIL]);
customer.add(temp_customer);
customer_name_list.add(dataset[a][CLIENT_NAME]);
}
}
Thank you for your help
Taking this as the line the exception occurs on:
while(!customer.get(temp_index).name.equals(dataset[a][CLIENT_NAME])){
There's a number of things that could be calling the issue. First off, customer could be null. Second, the results of customer.get(temp_index) could be null. Finally, customer.get(temp_index).name could also be null.
Since we're not working with the full code set here, I suggest you step through and print out the values to each of the above things to work out what one's null (or use a debugger.) That way you'll be able to see exactly what's causing the exception. My guess is that a customer's name may well be set to null which would cause the issue, but it could just as easily be any of the other things I mentioned.
If it isn't found you don't exit the loop, instead you keep going. Try adding a counter or clause so you don't go over.
Either your customer collection, or one of its elements, or the name field of one of its elements is null. I can't really say anything more without seeing what goes in the customer collection.
But a good idea is to print out temp_index in the loop in some kind of log file so you can see how many iterations does it complete before falling over.
Another thing you might find useful is to try to avoid multiple dereferencing operators (.) on the same line, especially if you know little about the data that is being accessed.
You could create a helper method for example:
private String getCustomerName( int index ) {
Customer c = customer.get( index );
return c.name;
}
And use it in your while loop.
Although unrelated, but as others have pointed out, your loop will end in an IndexOutOfBoundsException if no matching elements are found, so you'll have to fix that too.
for (dataA : dataset) {
if (customer_name_list.contains(dataA[CLIENT_NAME])) {
for (c : customer) if (dataA[CLIENT_NAME].equals(c.name)) {
c.add_plan(dataA[PLAN], dataA[DETAIL]);
break;
}
} else {
customer.add(new Customer(dataA[CLIENT_NAME], dataA[PLAN], dataA[DETAIL]));
customer_name_list.add(dataA[CLIENT_NAME]);
}
}
That's what you're doing, only cleaner. I suggest you might want to switch to either a hashing data structure (e.g., a HashMap) or a sorted data structure to improve your lookup and search calls. Then the above would look like
for (data : dataset) {
if ((Customer c = customer_map.get(data[CLIENT_NAME]))!=null) {
c.add_plan(data[PLAN], data[DETAIL]);
} else {
customer_map.put(data[CLIENT_NAME], new Customer(data[CLIENT_NAME], data[PLAN], data[DETAIL]));
}
}
which, in addition to be easier on the brain, is nicer performance-wise. If you need other stuff, like insert-order based iteration, you can use the appropriate map flavor.
As to the NPE, that means an item you've fetched isn't properly initialized - either the item at temp_index is null (and getting name throws the NPE), or name is null and trying to call equals on it throws the NPE. If the index weren't there, you'd be getting an out-of-bounds style exception.
I've run into this while writing a Traveling Salesman program. For an inner loop, I tried a
for(Point x:ArrayList<Point>) {
// modify the iterator
}
but when adding another point to that list resulted in a ConcurrentModicationException being thrown.
However, when I changed the loop to
for(int x=0; x<ArrayList<Point>.size(); x++) {
// modify the array
}
the loop ran fine without throwing an exception.
Both a for loops, so why does one throw an exception while the other does not?
As others explained, the iterator detects modifications to the underlying collection, and that is a good thing since it is likely to cause unexpected behaviour.
Imagine this iterator-free code which modifies the collection:
for (int x = 0; list.size(); x++)
{
obj = list.get(x);
if (obj.isExpired())
{
list.remove(obj);
// Oops! list.get(x) now points to some other object so if I
// increase x again before checking that object I will have
// skipped one item in the list
}
}
The first example uses an iterator, the second does not. It is the iterator that checks for concurrent modification.
the first code is using an iterator so modifying the collection is not allowed. The second code you are accessing each object with x.get(i), so not using an iterator, modifications thus are allowed
You cannot modify a List while you are iterating over it which you are doing in the first example. In the second you simply have a regular for loop.
If you run the code and observe you find that first iteration of the loop works fine but the second throws ConcurrentModicationException
if is because next() method checks if the number of the elements did not change.
For nice explanation see http://javaadami.blogspot.com/2007/09/enhanced-for-loop-and.html
I am developing a 2D grid based sim game. Progress is good.
Been cracking out code for the last few days really well, and I've been having some issues today.
I build two rooms, set the door position, and spawn a person. The person has the 2 room types that they must visit in order to be cured (It's a remake of Theme Hospital).
I click the spawn button, Person moves onto the grid from off screen, and then when the spawn is finished, they get their first path to the first room, then second, and then back to the ending spawn point.
When I say the spawn point, I mean the point off screen / off grid, when I say the ending spawn point, I mean the point where the person is when the spawn cycle is finished, and they are fully on screen / grid.
The problem now comes, what to do with the "person" (which is an object in its own right), is finished with? Should I simple set a boolean so they are no longer "active" but data on them remains? I wasn't sure, so I tried to just remove them from the ArrayList of people objects they were being held in.
I did this by assigning a number value to each person on creation which was the same as their new position in the ArrayList, and then when removing them, using that number. I know that in itself is floored, but I'm only testing with 1 person right now. If anyone can give me some help on that, fantastic! But I suppose that is more a sub question.
When the person is to be removed, they are, and I check the size of the arraylist, and then I get the following error... "Exception in thread "AWT-EventQueue-0" java.util.ConcurrentModificationException"
I did some researching and I understand this is to do with synchronisation of objects in different threads, but I'm not sure that I have more than one thread. I do have a single timer, and I don't think the people ArrayList is accessed elsewhere.
Any Thoughts? Ideas? Suggestions? Comments on this?
Thanks in advance!
(I will soon post a video and update this question with a link to it)
Typically I give every person or entity or whatever a boolean field called isMarkedForDeletion. Then during the main loop, they will either be told to update(), or if they are marked for deletion their index will be added to a deletion list. Once you have the deletion list all populated, you can iterate through the deletion list and remove those objects from the main list. Alternatively you can just iterate backwards and pull from the tail end of the list.
This may or may not explain your ConcurrentModificationException - I've never gotten them when working with ArrayList's before. However, I've gotten them from LinkedList's and it was precisely for the reason you described and had nothing to do with threads. The actual cause was using an Iterator. The following will cause a ConcurrentModificationException:
for (Iterator i = list.iterator(); i.hasNext();)
{
Person o = i.next();
o.update();
if (list.isMarkedForDeletion())
{
list.remove(o);
}
}
This will not cause an Exception:
ArrayList deletedObjects = new ArrayList();
for (Iterator i = list.iterator(); i.hasNext();)
{
Person o = i.next();
o.update();
if (list.isMarkedForDeletion())
{
deletedObjects.add(o);
}
}
for (int i = 0; i < deletedObjects.size(); i++)
{
list.remove(deletedObjects.get(i));
}
Perhaps the simplest way to do it, will not cause an Exception:
for (int i = list.size()-1; i >= 0; i--)
{
Person o = list.get(i);
if (o.isMarkedForDeletion())
{
list.remove(i);
}
else
{
o.update();
}
}
Just a wild guess due to the lack of any code excerpts, but most probably your rendering is iterating through the ArrayList on Swing's thread, while you another thread (the timer I guess?) tries to remove the person from the list.
If that's the case, one way to go is to have Swing remove the person from the list (between two renderings) as:
// peopleArray and personInstance should be final
Runnable removePersonTask = new Runnable() {
public void run() {
peopleArray.remove(personInstance).
}
};
SwingUtilities.invokeLater(removePersonTask).
One of the more common reasons for this problem is because you can not modify an ArrayList while you are iterating over it.
Also, since you have a GUI, you have at least two threads. Swing and AWT run on a separate thread. So if the GUI is directly accessing the ArrayList, there might be some problems there.
But without code, it's not possible to tell for sure what the problem is or might be.