尽管HashMap被恢复到原始状态,但并发修改异常?

时间:2017-02-15 17:50:05

标签: java exception

我目前正在开发一个软件工程类型类的项目,并遇到我认为奇怪的错误。根据我已经完成的研究/调试,当 next()方法修改HashMap时,通常会抛出Java ConcurrentModificationException 被称为。

为了让你了解问题领域,这是一个国际象棋游戏,我决定将死。目前我的代码结构如下:

  1. 获取HashMap(主板)的所有密钥
  2. for(Coordinate key:keys) ...
  3. Piece 移至船上
  4. 检查King是否受到攻击
  5. 移回棋子
  6. 循环
  7. 这段代码在循环时生成 ConcurrentModificationException ,尽管HashMap处于与迭代开始时相同的状态 - 大小和精确值。

    任何可能导致这种情况的想法?

    循环:

    for(ValidatorCoordinate key: keys){
        if(board.findPiece(key).getPieceType() != XiangqiPieceType.GENERAL 
            && moveValid(key, spaceBetween, color)
            && tryBlock(key, spaceBetween, dst, color)){
                    return true;
        }
    }
    

    tryBlock()(修改HashMap的方法)

    private boolean tryBlock(ValidatorCoordinate source, 
        ValidatorCoordinate destination,
        ValidatorCoordinate underAttack, XiangqiColor c){
    
            board.movePiece(source, destination, c);
            boolean blocked = !underAttack(underAttack, c);
            board.movePiece(destination, source, c);
            return blocked;
    }
    

    movePiece()方法肯定会在HashMap中移动片段(此时已经过彻底测试)

    非常感谢任何帮助。

    谢谢!

    编辑:为了澄清,这种通用方法一直有效,直到我将移动抽象为另一种方法( tryBlock )。以前, tryBlock 的内容都在循环内部,并且没有抛出任何异常。这也是我关注这一点的原因,对我而言,它本应该抛出异常。

3 个答案:

答案 0 :(得分:0)

您是否考虑过从HashMap切换到ConcurrentHashMap?后者允许对映射进行并行读取,并避免许多并发异常,这些异常异常通常是由共享映射的线程代码导致的(不完全是您描述的内容)。

正如评论者指出的那样,即使您反转了内容(例如添加/删除或删除/添加),您也可能会遇到并发异常。

答案 1 :(得分:0)

出现问题是因为您正在迭代地图的键集,并在循环中修改地图。地图不会跟踪您将其重新置于同一状态的事实。对映射的任何结构修改都会使现有迭代器无效。

相反,迭代密钥集的副本,例如将元素放入List以确保密钥以相同的顺序迭代,而不是直接使用密钥集:

for (ValidatorCoordinate v : new ArrayList<>(keys)) {
  // ...
}

这是与键集完全独立的集合,因此对映射的修改对迭代器没有影响。

答案 2 :(得分:0)

最简单的答案是调整tryBlock(..)的实现,这样你只有在没有被阻止的情况下才会移动[而不是在被阻止的情况下尝试撤消移动]

private boolean tryBlock(ValidatorCoordinate source, 
ValidatorCoordinate destination,
ValidatorCoordinate underAttack, XiangqiColor c){

    boolean blocked = !underAttack(underAttack, c);
    if (!blocked) 
        board.movePiece(source, destination, c);

    return blocked;

}