2017-02-15 6 views
1

私は現在、ソフトウェアエンジニアリングタイプのクラスのプロジェクトに取り組んでおり、奇妙なエラーとして表示されています。私がJavaのConcurrentModificationExceptionを行った研究/デバッグによると、next()メソッドが呼び出されるまでにHashMapが変更されたときに、通常がスローされます。HashMapが元の状態に復元されているにもかかわらず、同時変更例外?

問題ドメインのアイデアを伝えるために、これは私が長男を決定しているチェスゲームです。現在、次のように私のコードが構成されています

  1. は(キー座標:キー)のためにはHashMap(ボード)
  2. のすべてのキーを取得します ...
  3. ボード上で発見する移動 ワンピースは、
  4. チェックは王が攻撃バック
  5. 移動ピースの下にある場合
  6. ループ

このセクションでは、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()(ハッシュマップが変更された方法)

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

問題は、マップのキーセットを反復してループ内のマップを変更しているために発生します。地図では、同じ状態に戻しているという事実は追跡されません。マップを構造的に変更すると、既存のイテレータは無効になります。

代わりに、キーセットのコピーを繰り返します(例:

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

これは、キーセットから完全に分離回収され、マップに変更が影響を及ぼさないように:Listに要素を置くことキーが直接設定キーを使用して、VS同じ順序で繰り返されていることを確認しますイテレータ

+0

チャームのように働いた!私は現在の機能性に基づいて問題を起こさないので、今は残しておき、期日が許せばそれをリファクタリングしていくつもりです。ありがとう! – bwbonanno

0

最も簡単な答えは

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; 

}

あなたがブロックされていない場合にのみ移動するようtryBlock(..)の実装を微調整[のではなく、あなたがブロックされている場合は、移動を元に戻すしようとしている]ことです
+0

ああ、そうです。申し訳ありませんが、コンテキストをもう少し明確にすべきです。残念ながら、アイデアは私が試合でチェックメイトをチェックしているということです。基本的には、小片が*小切手をブロックするために動くことができれば、私はゲームを終わりとしてマークしません。これをテストするために、私はピースを動かし、王がまだ攻撃を受けているかどうかをチェックします。このようにすると、キングをブロックするようにピースを動かすなどの攻撃から保護し、別の攻撃ピースを明らかにするだけです。しかし、フィードバックをありがとう! – bwbonanno

関連する問題