2013-09-10 2 views
0

J'écris un programme qui est fait pour résoudre le jeu Mastermind. L'essentiel du programme est de prendre une liste de toutes les solutions possibles et après chaque supposition qui n'est pas la bonne, supprimer quelque chose de la liste qui ne donnerait pas au moins cette solution. Cette méthode est faite pour comparer deux chaînes (guess et strFromArray) pour voir si elles ont les mêmes valeurs. Cependant, je reçois une erreur et je n'arrive pas à comprendre pourquoi. Toute aide serait appréciée.Je ne peux pas comprendre pourquoi j'ai cette erreur: ConcurrentModificationException

Exception in thread "main" java.util.ConcurrentModificationException 
at java.util.ArrayList$Itr.checkForComodification(Unknown Source) 
at java.util.ArrayList$Itr.next(Unknown Source) 
at Game.shortenTheList(Game.java:88) 
at Game.guess(Game.java:76) 
at Game.play(Game.java:40) 
at Game.main(Game.java:23) 


/* 
* Compares the two strings. If they would get the same output, return false. If they would get a different output, return true. 
*/ 
public boolean compare(String guess, String strFromArray, int black, int white) 
{ 
    int white2 = 0; 
    int black2 = 0; 

    char[] arr1 = guess.toCharArray(); 
    char[] arr2 = strFromArray.toCharArray(); 

    for(int i=0; i<guess.length(); i++) 
    { 
     if(arr1[i] == arr2[i]) 
     { 
      black2 ++; 
      arr1[i] = '$'; 
      arr2[i] = '%'; 
     } 
    } 

    for(int i=0; i<guess.length(); i++) 
    { 
     for(int j=0; j<strFromArray.length(); j++) 
     { 
      if(arr1[i] == arr2[j]) 
      { 
       white2++; 
       arr1[i] = '!'; 
       arr2[j] = '@'; 
      } 
     } 
    } 

    if(black == black2 && white == white2) 
     return false; 

    else 
     return true; 
} 

/* 
* Shortens the list of possible solutions by eliminating everything that wouldn't get at least the given output. 
*/ 
public void shortenTheList(String guess, int black1, int white1) 
{ 
    for (String str : possibleSolutions) 
    { 
     if(compare(guess, str, black1, white1)) 
     { 
      possibleSolutions.remove(str); 
     } 
    } 
} 
+1

Pouvez-vous le prouver provient de cet extrait de code? –

+2

Finny, pas d'itérateur en cours d'utilisation. Accédez-vous à cela via plusieurs threads? Pouvez-vous poster au moins les 4 premières lignes du stacktrace? – hexafraction

+0

Veuillez toujours envoyer la trace de la pile lorsque vous posez des questions sur une exception. – chrylis

Répondre

8

Une fois que vous ouvrez un Iterator (que vous faites implicitement lors de l'utilisation for(String str: possibleSolutions), toute modification de la collection sous-jacente (possibleSolutions), sauf en appelant remove sur la Iterator provoquera une ConcurrentModificationException, ce qui est très clairement documenté sur la collecte cours

Si vous avez besoin de supprimer des éléments de la collection, utilisez un explicite Iterator:.

Iterator<String> it = possibleSolutions.iterator(); 
while(it.hasNext()) { 
    if(compare(guess, it.next(), black1, white1)) 
     it.remove(); 
} 

Comme l'a souligné @allprog, une approche fonctionnelle est préférable lorsque vous avez un problème de "filtrage" aussi clair. Jusqu'à Java 8 est une option, en utilisant Iterables#filter de Guava ou Iterables#removeIf est probablement un bon choix; vous simplement envelopper votre méthode compare et le transmettre dans.

+1

Ce serait encore mieux si 'remove()' n'était pas appelé du tout. L'utilisation d'une approche fonctionnelle lorsque la liste est filtrée plutôt que modifiée est généralement une solution plus sûre. La classe 'Iterables' de Google Guava est une" mine d'or "des meilleures pratiques à cet égard. – allprog

2
/* 
* Shortens the list of possible solutions by eliminating everything that wouldn't get at least the given output. 
*/ 
public void shortenTheList(String guess, int black1, int white1) 
{ 
    for (String str : possibleSolutions) 
    { 
     if(compare(guess, str, black1, white1)) 
     { 
      possibleSolutions.remove(str); 
     } 
    } 
} 

Il y a votre problème.

Au lieu de cela, utilisez:

/* 
* Shortens the list of possible solutions by eliminating everything that wouldn't get at least the given output. 
*/ 
public void shortenTheList(String guess, int black1, int white1) 
{ 
    Iterator<String> it = possibleSolutions.iterator(); 
    while(it.hasNext()) 
    { 
     String str = it.next(); 
     if(compare(guess, str, black1, white1)) 
     { 
      it.remove(); 
     } 
    } 
} 

C'est le seul moyen propre pour supprimer un objet d'une collection nous parcourons actuellement plus. L'autre, moins élégante serait de créer une liste séparée de String pour supprimer et itérer sur cela.

1

Vous ne pouvez pas modifier la liste possibleSolutions pendant que vous parcourez la boucle foreach.

changer votre code à quelque chose comme ceci:

public void shortenTheList(String guess, int black1, int white1) 
{  
    for(Iterator<String> it = possibleSolutions.iterator(); it.hasNext()){ 
     String str = it.next(); 
     if(compare(guess, str, black1, white1)){ 
      it.remove(); 
     } 
    } 
} 
0

Comme le dit chrylis, vous ne pouvez pas supprimer un élément d'une collection sans utiliser un Iterator.

exemple:

public void shortenTheList(String guess, int black1, int white1) 
{ 
for (String str : possibleSolutions) 
{ 
if(compare(guess, str, black1, white1)) 
{ 
possibleSolutions.remove(str); 
} 
} 
} 

devrait être:

public void shortenTheList(String guess, int black1, int white1) 
{ 
Iterator i = possibleSolutions.Iterator(); 
while(i.hasNext()) 
{ 
String str = (String) i.next(); 
if(compare(guess, str, black1, white1)) 
{ 
i.remove(); 
} 
} 
} 
Questions connexes