Home > OS >  java.util.ConcurrentModificationException when using Iterator
java.util.ConcurrentModificationException when using Iterator

Time:10-27

I am having a problem with this iterator on the set. This is a small project that I created with spring Initializer.

I use spring boot with tomcat. Previously I used a for loop and then I saw this answer over here Iterating through a Collection, avoiding ConcurrentModificationException when removing objects in a loop and tried to adapt the first answer to my case.

Only thing is is that I have to decouple the 2 entities Parent and Child from the bidirectional one-to-many, many-to-one relationship before deleting. I am still getting this error even if I use the iterator.

@PostMapping("/savecart")
    public ResponseEntity<String> saveCartToDb(@RequestBody Set<CartProduct> cartProductList, Principal principal) {
        User logedInUser = userService.findUserByUsername(principal.getName()).get();

        Set<CartProduct> cartItemList = logedInUser.getCartProduct();

        for (CartProduct cartProduct : cartProductList) {
            cartProduct.setUser(logedInUser);
        }
        userService.saveNewUser(logedInUser);
        log.info("In saveCartToDb()");
        if (cartItemList.isEmpty()) {
            logedInUser.setCartProduct(cartProductList);
            userService.saveNewUser(logedInUser);
        } else {
            for (Iterator<CartProduct> iterator = cartItemList.iterator(); iterator.hasNext();) {
                CartProduct cartItem = iterator.next();
                if (cartItem != null) {
                    // Remove the current element from the iterator and the list.
                    cartItem.dismissParent();
                    logedInUser.dismissChild(cartItem);
                    iterator.remove();
                    userService.saveNewUser(logedInUser);

                }
            }
//            for(CartProduct cartItem : cartItemList){
//                cartItem.dismissParent();
//                logedInUser.dismissChild(cartItem);
//                userService.saveNewUser(logedInUser);
//            }
            cartProductService.deleteAllProductIds();
            for (CartProduct cartProduct : cartProductList) {
                cartProduct.setUser(logedInUser);
            }
            logedInUser.setCartProduct(cartProductList);
            userService.saveNewUser(logedInUser);
        }
        return ResponseEntity.ok().body(cartProductList.toString());
    }

The methods that I use to decouple

 public void dismissChild(CartProduct child) {
        this.cartProduct.remove(child);
    }

public void dismissParent() {
        this.user.dismissChild(this);
        this.user = null;
    }
java.util.ConcurrentModificationException: null
    at java.base/java.util.HashMap$HashIterator.remove(HashMap.java:1611) ~[na:na]

Is there a way I can modify the iterator no not receive this error anymore? Or is there a better solution that with the iterator?

CodePudding user response:

According to your exception

java.util.ConcurrentModificationException: null
    at java.base/java.util.HashMap$HashIterator.remove(HashMap.java:1611) [na:na]

So you have used a HashMap and you have received a ConcurrentModificationException.

But wait you don't have any HashMap in your code near the error correct?

You have cartItemList.iterator() and cartItemList is declared as Set<CartProduct>. Also the implementation which you have provided would be a HashSet.

But HashSet is implemented behind the scenes from java using a HashMap. Therefore the error that you have mentions a HashMap.

From documentation

public class ConcurrentModificationException extends RuntimeException

This exception may be thrown by methods that have detected concurrent modification of an object when such modification is not permissible. For example, it is not generally permissible for one thread to modify a Collection while another thread is iterating over it. In general, the results of the iteration are undefined under these circumstances. Some Iterator implementations (including those of all the general purpose collection implementations provided by the JRE) may choose to throw this exception if this behavior is detected. Iterators that do this are known as fail-fast iterators, as they fail quickly and cleanly, rather that risking arbitrary, non-deterministic behavior at an undetermined time in the future.

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.

HashMap and also HashSet provide a fail-fast iterator, meaning you are not allowed to modify the collection during iteration.

To solve your issue you should avoid modifying the collection during iteration.

So the following block of code

for (Iterator<CartProduct> iterator = cartItemList.iterator(); iterator.hasNext();) {
                CartProduct cartItem = iterator.next();
                if (cartItem != null) {
                    // Remove the current element from the iterator and the list.
                    cartItem.dismissParent();
                    logedInUser.dismissChild(cartItem);
                    iterator.remove();
                    userService.saveNewUser(logedInUser);

                }
            }

Could be rewritten as

List<CartProduct> toBeRemoved = new ArrayList(); <-----------
for (Iterator<CartProduct> iterator = cartItemList.iterator(); iterator.hasNext();) {
                CartProduct cartItem = iterator.next();
                if (cartItem != null) {
                    // Remove the current element from the iterator and the list.
                    cartItem.dismissParent();
                    logedInUser.dismissChild(cartItem);
                    toBeRemoved.add(cartItem);  <---------------
                    userService.saveNewUser(logedInUser);

                }
            }
cartItemList.removeAll(toBeRemoved); <--------

So you just gather all the elements during iteration that you need to remove and you invoke the removal only after iteration.

  • Related