避免静态集合并发修改的良好做法

Good practice to avoid ConcurrentModification of static collection

考虑 class 玩家...当玩家加入游戏(创建对象)时,它会检查是否已加入同名玩家...

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);
    }
}

我知道 Iterator 可以处理这个问题,但我们并不总是知道 public 外部方法中的静态字段会发生什么情况以及何时使用 foreach 以及何时使用 Iterator 代替...

这个问题有什么好的做法吗?

第一个也是更重要的良好实践称为关注点分离。如:Player class 应该模拟单个 player.

您在一个地方混合作为玩家的责任和管理整个玩家对象集。不要那样做!

这两件事根本不属于一起。从这个意义上说:应该有一个 PlayerManager class 例如 知道 所有玩家。并且也忘记像这样使用 static 字段。因为这会在 class 的不同方面之间产生超紧密的耦合。例如,当您需要 多个 玩家列表时会发生什么情况?如果您有太多玩家想要根据某些属性将他们组织到桶中怎么办?

除此之外,直接的答案是:与其立即 删除列表中的对象,不如将它们收集到第二个playersToBeDeleted 列表中。 迭代第一个列表后,只需使用 players.removeAll(playersToBeDeleted) 例如。

并谈论好的做法:仔细考虑你是否真的想使用 Lists - 或者如果 Set 不是更好选择。列表总是暗示顺序,糟糕的是,它们允许重复添加 same 对象。而 Set 免费为您提供 "unique elements" 语义!

我看到您仍在 for-each 块内尝试调用 list.remove(entry) 方法。不要那样做。

在需要时使用 Iterator 而不是 for-each 结构:

  1. 删除当前元素。 for-each 结构隐藏了迭代器,所以你不能调用 remove。因此,for-each 结构不可用于过滤。
  2. 并行迭代多个集合。

请注意,Iterator.remove 是在迭代期间修改集合的唯一安全方法;如果在迭代过程中以任何其他方式修改基础集合,则行为未指定。