Solved ConcurrentModificationException

Discussion in 'Spigot Plugin Development' started by ExpDev, May 14, 2017.

  1. Are someone aware why this produces a ConcurrentModificationException, but only when the inventory actually contains a golden apple. I've read up on it, but no info found.

    Code (Text):
    @EventHandler(priority = EventPriority.NORMAL)
    public void onDrop(EntityDeathEvent e) {
        // Get the dropped item stacks
        List<ItemStack> drops = e.getDrops();

        // Remove all dropped items but golden apples
        Iterator<ItemStack> itr = drops.iterator();
        while (itr.hasNext()) {
            ItemStack is = itr.next();
            if (is.getType() == Material.GOLDEN_APPLE) {
                drops.remove(is);
            }
            itr.remove(); // avoid ConcurrentModificationException
        }
    }
    Code (Text):
    at java.lang.Thread.run(Unknown Source) [?:1.8.0_121]
    Caused by: java.util.ConcurrentModificationException
            at java.util.ArrayList$Itr.checkForComodification(Unknown Source) ~[?:1.8.0_121]
            at java.util.ArrayList$Itr.remove(Unknown Source) ~[?:1.8.0_121]
    To solve, a simple line like this could be used:
    Code (Text):
    // Remove all dropped items but golden apples
    // avoid ConcurrentModificationException
    drops.removeIf(is -> is.getType() != Material.GOLDEN_APPLE);
    This is, as people mentioned in the replies, removing from the iterator without directly removing from the List, hence avoiding the infamous ConcurrentModificationException.
     
    #1 ExpDev, May 14, 2017
    Last edited: May 14, 2017
  2. Concurrent modification exceptions occur if you try to add or remove items to a list/map etc while you're iterating over it.

    Since you're doing drops.remove(is) this is causing it only add/remove elements once you've finished looping through the elements. Or use iterator.remove()


    Sent from my iPhone using Tapatalk
     
  3. WAS

    WAS

    I had this issue before. Not sure what exactly was happening as I was just trying to help someone else so I just added the item from getDrops that matches to a new collection, and then used removeAll() on getDrops()

    Also your codes commenting says it is suppose to keep only golden apples, but appears to be removing them?

    Maybe try something like

    Code (Java):
    List<ItemStack> blacklist = new ArrayList<ItemStack>();
    for (ItemStack item : e.getDrops()) {
      if (!item.getType().equals(Material.GOLDEN_APPLE))
        blacklist.add(item);
    }
    if (blacklist.size() > 0)
      e.getDrops().removeAll(blacklist);
     
    #3 WAS, May 14, 2017
    Last edited: May 14, 2017
  4. That's a typo. Thanks for pointing that out. A bit tired.

    So using itr.remove() on the iterator will directly also remove the specific item stack from the list as a whole?
     
  5. electronicboy

    IRC Staff

    you're creating an iterator, and then trying to remove the items from the original collection... You should only be removing them via the iterator, not modifying the original collection manually. The iterator will throw an exception whenever you try to do anything with it next as it will try to verify the state of the collection and see that it has been modified.
     
  6. Yeah, I just figured that out. It's solved. Thank you.
     
  7. This sufficed:
    To solve, a simple line like this could be used:
    Code (Text):
    // Remove all dropped items but golden apples
    // avoid ConcurrentModificationException
    drops.removeIf(is -> is.getType() != Material.GOLDEN_APPLE);
     
    • Like Like x 1
  8. WAS

    WAS

    Nice, much cleaner.