简体   繁体   中英

Good practice to avoid ConcurrentModification of static collection

Consider class of Player... when player joins the game (object is created), it checks for player with the same name already joined...

public class Player {

    private static List<Player> players = new ArrayList<>();
    private String name;

    public Player(String name) {
        this.name = name;

        for (Player otherPlayer : players) { // Iterating static field
            if (otherPlayer.name.equalsIgnoreCase(name)) {
                otherPlayer.quit("Somebody with the same name joined the game");
            }
        }
    }

    public void quit(String message) {
        players.remove(this); // Modifying static field
        Server.disconnect(this, message);
    }
}

I know that Iterator can deal with this problem, but we don't always know what happens with public static fields in foreign methods and when to use foreach and when to use Iterator instead...

Is there any good practice for this problem?

The first, and more important good practice is called separation of concerns . As in: the Player class should model a single player .

You are mixing the responsibility of being a Player and managing the overall set of Player objects in one place. Don't do that!

These two things simply don't belong together. In that sense: there should be a PlayerManager class for example that knows about all players. And also forget about using a static field like this. Because that creates super-tight coupling between the different aspects of your class. What happens for example when you need more than one list of players? What if you have so many players that you want to organize them in buckets, based on certain properties?

Beyond that, the direct answer is: instead of immediately deleting objects from your list - collect them into a second playersToBeDeleted list. And after iterating the first list, simply use players.removeAll(playersToBeDeleted) for example.

And talking about good practices: carefully consider if you really want to use Lists - or if Set wouldn't be the better alternative. Lists always imply order, and yuck, they allow to repeatedly add the same object. Whereas a Set gives you "unique elements" semantics for free!

I see that you are trying to call list.remove(entry) method while still inside the for-each block. Don't do that.

Use Iterator instead of the for-each construct when you need to:

  1. Remove the current element. The for-each construct hides the iterator, so you cannot call remove. Therefore, the for-each construct is not usable for filtering.
  2. Iterate over multiple collections in parallel.

Note that Iterator.remove is the only safe way to modify a collection during iteration; the behavior is unspecified if the underlying collection is modified in any other way while the iteration is in progress.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM