简体   繁体   中英

Any easy way to deal with ConcurrentModificationException in this java game?

I was trying to build a simple Rock-Paper-Scissors game in Java. Basically, the idea is that players get eliminated when they make the wrong move. The final player remaining is the winner. I am relatively inexperienced and don't know too much. I ran into this ConcurrentModification exception when I ran it. I tried to Google a bit, but didn't get any easy fix. There were discussions about things like iterators which I don't quite know/understand.

I tried it like below-

GameStarter class:

public class GameStarter
{
    public static void main(String[] args)
    {

    RPS g = new RPS();
    g.play();
    }
}

RPS class:

import java.util.ArrayList;
public class RPS
{
   Player p1 = new Player(" A ");
   Player p2 = new Player(" B ");
   Player p3 = new Player(" C ");
   Player p4 = new Player(" D ");
   Player p5 = new Player(" E ");
   ArrayList<Integer> hold =new ArrayList<Integer>();
   ArrayList<Player> active = new ArrayList<Player>();
   {
    active.add(p1);
    active.add(p2);
    active.add(p3);
    active.add(p4);
    active.add(p5);
        }
   public void play()
   {
    int i,j;
    while(active.size()>1)
    {

     for(Player p:active)
    {
    System.out.print("\n Currently active players are: "+p.name+" , ");
    }   
    System.out.println("\n\n");
    for(Player p:active)
    {
    p.rpsThrow();
    hold.add(p.cur);
    }

    if(hold.contains(1) && hold.contains(2) && !hold.contains(3))
    {
    for(Player p: active)
    {
        if(p.cur==1)
        {
        active.remove(p);
        System.out.println("Player "+p.name+" eliminated ");
        }
    }

    }
    if(hold.contains(1) && !hold.contains(2) && hold.contains(3))
    {
    for(Player p: active)
    {
    if(p.cur==3)
        {
        active.remove(p);
        System.out.println("Player "+p.name+" eliminated ");
        }        
    }

    }
    if(!hold.contains(1) && hold.contains(2) && hold.contains(3))
    {
    for(Player p: active)
    {
        if(p.cur==2)
        {
        active.remove(p);
        System.out.println("Player "+p.name+" eliminated ");
        }
    }

    }

    hold.clear();
    try
    {
    Thread.sleep(3500);
    }
    catch(Exception ex)
    {

    }
}
if(active.size()==1)
{
for(Player p:active)
{
System.out.println("\n\n The winner is : \n"+p.name);
}
}


    }
}

and Player class:

public class Player
{
  String name;
  Integer cur;

  public Player(String n)
  {
    name=n;
   }
   public int rpsThrow()
   {
    int t;
    t=(int)((Math.random()*3)+1);
    cur=t;
    if(t==1)
    {
        System.out.println("\n"+name+" throws : rock");
    }
    else if(t==2)
    {
        System.out.println("\n"+name+" throws : paper");
    }
    else
    {
        System.out.println("\n"+name+" throws : scissors");
    }
    return t;    
    }
}

Isn't there any easy way to solve this CMException? Please feel free modify my code to solve it.

Basically, once you start using an iterator on a Collection (and this type of for loop uses an iterator) you cannot modify the underlying collection until you have finished with the iterator, otherwise you will get a CME. If you want to remove items from a collection you need to use an explicit iterator and uses its remove() method, like this:

public static void main(String[] args) throws Exception {
    List<String> vals = new ArrayList<>();
    vals.add("1"); vals.add("2"); vals.add("3");

    // This throws a CME
    for(String s: vals) {
        if("1".equals(s)) {
            vals.remove(s);
        }
    }

    // This would work
    for(Iterator i = vals.iterator(); i.hasNext(); ) {
        if("1".equals(i.next())) {
            i.remove();
        }
    }
}

I changed RPS class to this(with normal for):

package RockPaperScissors;
import java.util.ArrayList;
public class RPS
{
   Player p1 = new Player(" A ");
   Player p2 = new Player(" B ");
   Player p3 = new Player(" C ");
   Player p4 = new Player(" D ");
   Player p5 = new Player(" E ");
   ArrayList<Integer> hold =new ArrayList<Integer>();
   ArrayList<Player> active = new ArrayList<Player>();
   {
    active.add(p1);
    active.add(p2);
    active.add(p3);
    active.add(p4);
    active.add(p5);
        }
   public void play()
   {
    int i,j;
    Player p;
    while(active.size()>1)
    {

     for(i=0;i<active.size();i++)
    {
      p=  active.get(i);
    System.out.print("\n Currently active players are: "+p.name+" , ");
    }   
    System.out.println("\n\n");
    for(i=0;i<active.size();i++)
    {
      p=  active.get(i);  
    p.rpsThrow();
    hold.add(p.cur);
    }

    if(hold.contains(1) && hold.contains(2) && !hold.contains(3))
    {
    for(i=0;i<active.size();i++)
    {
        p=  active.get(i);  
        if(p.cur==1)
        {
        active.remove(p);
        System.out.println("Player "+p.name+" eliminated ");
        }
    }

    }
    if(hold.contains(1) && !hold.contains(2) && hold.contains(3))
    {
    for(i=0;i<active.size();i++)
    {
        p=  active.get(i);  
    if(p.cur==3)
        {
        active.remove(p);
        System.out.println("Player "+p.name+" eliminated ");
        }        
    }

    }
    if(!hold.contains(1) && hold.contains(2) && hold.contains(3))
    {
     for(i=0;i<active.size();i++)
    {
        p=  active.get(i);  
        if(p.cur==2)
        {
        active.remove(p);
        System.out.println("Player "+p.name+" eliminated ");
        }
    }

    }

    hold.clear();
    try
    {
    Thread.sleep(3500);
    }
    catch(Exception ex)
    {

    }
}
if(active.size()==1)
{
for(i=0;i<active.size();i++)
{
p=  active.get(i);  
System.out.println("\n\n The winner is : \n"+p.name);
}
}


    }
}

The RPE is no longer there. Though I must say it is not quite behaving like I expect to it. I will also try to look into and study iterators. Thanks for all your help. EDIT:changed the indices with normal for. Now it works like I wanted.

package RockPaperScissors;
import java.util.ArrayList;
public class RPS
{
   Player p1 = new Player(" A ");
   Player p2 = new Player(" B ");
   Player p3 = new Player(" C ");
   Player p4 = new Player(" D ");
   Player p5 = new Player(" E ");
   ArrayList<Integer> hold =new ArrayList<Integer>();
   ArrayList<Player> active = new ArrayList<Player>();
   {
    active.add(p1);
    active.add(p2);
    active.add(p3);
    active.add(p4);
    active.add(p5);
        }
   public void play()
   {
    int i,j;
    Player p;
    while(active.size()>1)
    {

     for(i=0;i<active.size();i++)
    {
      p=  active.get(i);
    System.out.print("\n Currently active players are: "+p.name+" , ");
    }   
    System.out.println("\n\n");
    for(i=0;i<active.size();i++)
    {
      p=  active.get(i);  
    p.rpsThrow();
    hold.add(p.cur);
    }

    if(hold.contains(1) && hold.contains(2) && !hold.contains(3))
    {
    for(i=0;i<active.size();i++)
    {
        p=  active.get(i);  
        if(p.cur==1)
        {
        active.remove(p);
        **i=i-1;**
        System.out.println("Player "+p.name+" eliminated ");
        }
    }

    }
    if(hold.contains(1) && !hold.contains(2) && hold.contains(3))
    {
    for(i=0;i<active.size();i++)
    {
        p=  active.get(i);  
    if(p.cur==3)
        {
        active.remove(p);
        i=i-1;
        System.out.println("Player "+p.name+" eliminated ");
        }        
    }

    }
    if(!hold.contains(1) && hold.contains(2) && hold.contains(3))
    {
     for(i=0;i<active.size();i++)
    {
        p=  active.get(i);  
        if(p.cur==2)
        {
        active.remove(p);
        i=i-1;
        System.out.println("Player "+p.name+" eliminated ");
        }
    }

    }

    hold.clear();
    try
    {
    Thread.sleep(3500);
    }
    catch(Exception ex)
    {

    }
}
if(active.size()==1)
{
for(i=0;i<active.size();i++)
{
p=  active.get(i);  
System.out.println("\n\n The winner is : \n"+p.name);
}
}


    }
}

Use iterator in your loops, in which you modify the collection you currently iterate over

for(Iterator iter = active.iterator(); iter.hasNext();)
{
    Player p = (Player)iter.next();
    if(p.cur==1)
    {
    iter.remove();
    System.out.println("Player "+p.name+" eliminated ");
    }
}

更改活动播放器以循环到Iterator

This piece of code is problematic :-

for(Player p: active)
{
    if(p.cur==2)
    {
    active.remove(p);
    System.out.println("Player "+p.name+" eliminated ");
    }
}

you are iterating on list active and removing the element from it.

You need to use iterator and remove data from iterator, it will remove from actual list automaticallly.

  for(Iterator playerIterator = active.iterator(); playerIterator.hasNext();) {
   Player p = (Player)playerIterator.next();
   if(p.cur==2){
    playerIterator.remove();
   System.out.println("Player "+p.name+" eliminated ");
  }
 }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM