2017-02-15 34 views
1

我目前正在开发一个软件工程类型类的项目,并运行我认为是一个奇怪的错误。根据我已经完成Java的研究/调试ConcurrentModificationException通常在HashMap被修改时调用next()方法时被抛出。尽管HashMap被还原为原始状态,但并发修改异常?

为了给你一个问题域的想法,这是一个国际象棋游戏,我决定将死。

  1. 获取的HashMap(板)
  2. 的所有键(坐标键:键)目前如下我的代码的结构 ...
  3. 移动被发现在船上
  4. 检查国王是受到攻击
  5. 移动式靠背

尽管HashMap处于与迭代开始时相同的状态 - 大小和精确值,但这部分代码在循环时会生成ConcurrentModificationException

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

循环:

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的内容位于循环内部,并且不会引发异常。这也是为什么我关心这一点,至于我应该抛出这个例外。

+1

任何修改都被'HashMap'拒绝,即使它最终回到原始状态。 (出于很好的理由;即使地图内容相同,哈希桶的内部安排也可能不同。) –

+0

只是为了澄清,是否有任何理由说明为什么直到循环才会抛出此异常呢?这种修改在技术上是允许的,但循环抱怨。 此外,这种相同的方法以前工作,但片的移动并没有抽象成另一种方法在那一点。这个错误只出现在重构中。 谢谢! – bwbonanno

+0

'Iterator'方法通常是抛出ConcurrentModificationException的方法。修改方法不知道还有一个迭代仍在继续。 –

回答

0

你有没有考虑过从HashMap切换到ConcurrentHashMap?后者允许对映射进行并行读取并避免许多并发异常,这些异常通常是由线程代码共享映射(不完全如您所描述的)引起的。正如评论者所指出的,即使您反转事物(例如添加/删除或移除/添加),您也可能会遇到并发异常。

+0

我认为这是一个单线程环境..在这种情况下,这将无济于事。如果您使用迭代器并在迭代器之外修改集合,则会引发异常 - 这也会发生在线程安全的简单实现中。 –

0

问题出现是因为您正在迭代地图的关键集,并在循环中修改地图。地图不会跟踪您将其重置为相同状态的事实。对映射的任何结构修改都会使现有的迭代器失效。相反,迭代密钥集的副本,例如,把元件成List以确保密钥在相同的顺序重复VS使用密钥直接设置:

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

这是从密钥集合一个完全独立的收集,所以修改到地图有没有影响迭代器。

+0

工作就像一个魅力!不禁感到这样做可能是一种避免一些安全措施的解决方法,但是基于我目前的功能,它不会引起问题,所以我现在就把它留下,并在到期日允许的情况下重构它。谢谢! – bwbonanno

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; 

}

移动实现(..)
+0

啊是的。对不起,我应该更清楚一点。不幸的是,这个想法是,我正在检查游戏中的将军。基本上,如果一件棋子能够移动来阻止支票,那么我不会将比赛标记为结束。为了测试这个,我移动这个棋子并检查国王是否仍然受到攻击。这样做可以防止诸如移动棋子来阻挡国王之类的事情,只能揭示另一个攻击棋子。感谢您的反馈! – bwbonanno