0

I think my solution has been incorrectly mistaken with the solution of another question. It has also been edited so it looks like the solution is the one pointed by the duplicate, but this obviously did not solve my problem, just avoided the exception raised (the surface of the problem). Since my efforts to reopen the question have been useless, I have opened a new one here.


I am getting a ConcurrentModificationException and I am not able to solve this problem. So I remove a series of elements from a list and I get an error.

This is the error:

04-Nov-2016 15:49:50.488 SEVERE [http-nio-8080-exec-199] org.apache.catalina.core.StandardWrapperValve.invoke Servlet.service() for servlet [Faces Servlet] in context with path [/...] threw exception [java.util.ConcurrentModificationException] with root cause
 java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:886)
    at java.util.ArrayList$Itr.next(ArrayList.java:836)
    at com.ex.PersonController.removeGroup(...)
    at ....

The Java code:

public void removeGroup(int age) {
    if (person != null) {
        List<Person> friends = person.getFriends();
        List<Person> removeFriends = new ArrayList<>();
        if (friends != null) {
            for (Person p : friends) {
                if (p.getAge() == age) {
                    removeFriends.add(p);
                } 
            }
            friends.removeAll(removeFriends);
        }
    }

EDIT: Note that this apparently is not solving my problem. It avoids raising an error, though, but the results displayed by JSF are not correct. Is it possible that JSF is trying to refresh while my list is not fully updated?


EDIT2: I don't think this is a duplicate. The error I got with some of my tests is gone with this or this (note that even with the .add() approach I got the same error for some reason). But JSF is still not printing what I expect. I write again the code that BalusC removed from my question with a bit more of information:

The xhtml Java code:

<c:forEach items="#{personController.groupedPersons}" var="personG">
    <h:commandButton action="#{personController.removeGroup(personG.key)}" type="submit" value="Remove" >
        <f:ajax render="@form" />
    </h:commandButton>
    #{personG.key} - #{personG.value.size()}
</c:forEach>

So the first time I execute the page, personController.groupedPersons returns the right list of persons (a HashMap<Integer, List<Person>>) and prints it just fine. At this point I have 2 groups: a group of 3 Person of the same age and another Person with a different age. When I click on remove the group of the 3 persons with the same age, I trace the code and with the Iterator it removes all the necessary persons without raising a ConcurrentModificationException. The returned list of person.getFriends(); is size = 1, which is correct. Then the ajax code renders the form. personController.groupedPersons is called again and returns 1 person as expected. I have verified and this is the return person I actually expect. But then the JSF does print the wrong #{personG.key} (the one I removed) and null #{personG.value.size()}.

I know it might be difficult to follow, but can you think of any explanations for this?


EDIT 3: This is even funnier... If I delete the group that has 1 person, it is removed and then the JSF prints correctly the group with 3 persons. If I delete the group with 3 persons, they are removed and JSF prints (as I said in EDIT2) the key of the group I just removed and size() is null. Is it possible that I am on a concurrency problem here between JSF refreshing the page at the same time as I am modifying the list? (this was originally my worry, that the ConcurrentModificationException came from a concurrency problem between the XHTML and my managed bean, and not only within the code of the Managed Bean). That could explain why I got a ConcurrentModificationException even when I added in the list instead of removing from it.

Community
  • 1
  • 1
user1156544
  • 1,725
  • 2
  • 25
  • 51
  • 1
    You really should not edit your question and completely change it. It invalidates all of the answers and ruins the question for posterity. – Gray Nov 04 '16 at 17:14
  • I did not edit the question completely. It was BalusC, as you can see in the history. And I appreciate the editing, but I still think that maybe JSF has something to do (perhaps it doesn't and that's why he removed completely everything) – user1156544 Nov 05 '16 at 16:11
  • The concurrent modification exception has nothing to do with JSF. The fact that JSF does not print what you expect is a different question (like you posted https://stackoverflow.com/questions/40442340/jsf-wrong-display-of-elements-when-iterating-a-map) and that is also a 'duplicate'. JSF is behaving excactly as designed for how you use it. – Kukeltje Nov 07 '16 at 18:07
  • I think your comment makes it clear now, thanks. I know that the ConcurrentModification had nothing to do directly, but I thought that indirectly there could be a concurrency issue between JSF and my code (even I could not verify this successfully in any of my tests). Since JSF actually prints something, confused me. – user1156544 Nov 07 '16 at 18:39

1 Answers1

0

In java you are not allowed to iterate through a list (friends) and in the loop remove elements from it (friends.remove(p)). This throws a ConcurrentModificationException.

You can use an Iteratorinstead

Iterator<Person> iterator = friends.iterator();
while (iterator.hasNext()) {
    if (iterator.next().getAge() == age) {
        iterator.remove();
    }
}

EDIT:

Your approach with removeFriends.add(p); should work. You won't get a concurrent modification exception with this. You might however end up with an UnsupportedOperationException if your underlying list does not support removeAll. This is for example the case with Arrays.asList(...). If you wrap your friends list is a new ArrayList(friends); you should be good to go:

public void removeGroup(int age) {
  if (person != null) {
    List<Person> friends = new ArrayList<>(person.getFriends());
    List<Person> removeFriends = new ArrayList<>();
    if (friends != null) {
        for (Person p : friends) {
            if (p.getAge() == age) {
                removeFriends.add(p);
            } 
        }
        friends.removeAll(removeFriends);
    }
}

Don't forget to set the friends list after that:

person.setFriends(friends);
Erik
  • 2,888
  • 2
  • 18
  • 35
  • That is one of the tests I did, and you are right in there. But what about the approach I am using with `removeFriends.add(p);` It seems OK to me – user1156544 Nov 04 '16 at 16:20
  • Your suggestion avoids the exception. But I am not getting the right results... It seems that the code stops doing the loop in the middle for some reason. Or the list is not properly displayed back in the xhtml code... – user1156544 Nov 04 '16 at 16:31
  • Your approach with `removeFriends.add(p);` should work too. You won't get a concurrent modification exception with this. You might however end up with an `UnsupportedOperationException` if your underlying list does not support `removeAll`. This is for example the case with `Arrays.asList(...)`. If you wrap your `friends` list is a `new ArrayList(friends);` you should be good to go. – Erik Nov 04 '16 at 16:35
  • I know, but it didn't work... The wrong code was (as I said in my original unedited question) a second approach, but I got the Concurrent error with the .add() version. I suppose there is something else around between JSF and my code? – user1156544 Nov 04 '16 at 16:45