避免静态集合并发修改的良好做法
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 结构:
- 删除当前元素。 for-each 结构隐藏了迭代器,所以你不能调用 remove。因此,for-each 结构不可用于过滤。
- 并行迭代多个集合。
请注意,Iterator.remove
是在迭代期间修改集合的唯一安全方法;如果在迭代过程中以任何其他方式修改基础集合,则行为未指定。
考虑 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 结构:
- 删除当前元素。 for-each 结构隐藏了迭代器,所以你不能调用 remove。因此,for-each 结构不可用于过滤。
- 并行迭代多个集合。
请注意,Iterator.remove
是在迭代期间修改集合的唯一安全方法;如果在迭代过程中以任何其他方式修改基础集合,则行为未指定。