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.