2009-12-22 3 views
2

Ceci est mon code pour construire un tour possible de villes dans un Locale l (ce n'est pas optimal c'est juste pour donner à ma recherche AI ​​une longueur d'avance).pas sûr de raison pour ConcurrentModificationException

Je reçois un ConcurrentModificationException qui, à ma connaissance, arrive lorsque plusieurs parties de code accèdent à une variable/collection et tentent de la modifier. Causer ce code pour obtenir malheureux:

final void checkForComodification() { 
    if (modCount != expectedModCount) 
     throw new ConcurrentModificationException(); 
} 

je modifie comme je l'ajout d'un élément, mais comme le Iterator ne dispose pas d'une méthode pour ajouter (seulement la suppression) J'utilise la méthode de la collection.

Alors, mes questions sont les suivantes:

  1. est mon ajoutant l'élément ce qui cause le problème?
  2. Si c'est le cas, comment l'ajouter correctement pour que le modCount soit correct et que je n'obtienne pas le ConcurrentModificationException?

méthode complète ci-dessous, avec un commentaire sur la ligne où le ConcurrentModificationException arrive:

public void construct() { 
    tour = new ArrayList(); 
    ArrayList<City> lcl = new ArrayList(l.getCitys()); 

    tour.add(lcl.remove(0)); 
    tour.add(lcl.remove(1)); 

    while (!this.tourComplete()) { 
     System.out.println(tour.size()); 
     Iterator tourit = tour.iterator(); 
     City g1 = (City) tourit.next(); 
     City g2 = (City) tour.get(lcl.indexOf(g1)+1); 

     int gapDist = l.distanceBetweenCitys(g1, g2); 

     while (tourit.hasNext()) { 
      City C = null; 
      int best = Integer.MAX_VALUE; 

      for (Iterator lclit = lcl.iterator(); lclit.hasNext();) { 
       City c = (City) lclit.next(); 
       int avg = (l.distanceBetweenCitys(g1,c) + 
          l.distanceBetweenCitys(g2, c))/2 ; 

       if ((avg<gapDist) && (avg<best)) { 
        C = c; 
        best = avg; 
       } 
      } 

      if (C != null) { 
       assert(best == Integer.MAX_VALUE); 
       City A = tour.get(0); 
       City Z = tour.get(tour.size()-1); 

       boolean begin = true; 

       for (Iterator lclit = lcl.iterator(); lclit.hasNext();) { 
        City c = (City) lclit.next(); 
        int dist = l.distanceBetweenCitys(A,c); 

        if (dist<best) { 
         begin = true; 
         C = c; 
         best = dist; 
        } 
       } 

       for (Iterator lclit = lcl.iterator(); lclit.hasNext();) { 
        City c = (City) lclit.next(); 
        int dist = l.distanceBetweenCitys(Z,c); 

        if (dist<best) { 
         begin = false; 
         C = c; 
         best = dist; 
        } 
       } 

       if (begin) { 
        // one of these is causing the problem 
        tour.add(0,C); 
       } 
       else { 
        // one of these is causing the problem 
        tour.add(C); 
       } 
      } 
      else { 
       // one of these is causing the problem 
       tour.add(tour.indexOf(g2),C); 
      } 

      g1 = (City) tourit.next(); // this is where it all goes wrong 
      g2 = (City) tour.get(lcl.indexOf(g1)+1); 
      gapDist = l.distanceBetweenCitys(g1, g2); 
     } 
    } 
} 

Répondre

5

Vous ne pouvez pas modifier la collection sous-jacente tout en utilisant le iterator (sauf par l'itérateur lui-même).

Je n'ai pas passé par votre algorithme (vous semblez vouloir insérer à des positions arbitraires, ce qui pourrait être difficile), mais vous pouvez peut-être faire une des opérations suivantes:

  1. recueillir tout ce que vous voulez ajouter dans une deuxième collections, et faire un addAll après que vous avez terminé.

  2. Effectuez une itération sur une copie de la collection à la place.

  3. Utilisez un ListIterator qui a une méthode add en plus de remove.

  4. Ne pas utiliser un itérateur du tout, et accéder à tout l'ArrayList par index (que vous faites déjà dans d'autres endroits de toute façon)

En outre, vous pouvez en finir avec beaucoup de typecasts, en spécifiant le type de l'itérateur (comme vous l'avez fait avec la liste).

+0

Je pensais à 1 et 2 dès que j'ai vu l'exception Mon code consiste à insérer la meilleure ville entre chaque paire de villes jusqu'à ce que toutes les villes soient dans le tour donc elle doit se remettre après chaque ville ajouté que de nouvelles paires sont formées Il semblerait que 3 ids ma meilleure option, Il se résout à la difficulté maintenant. Alors que SO revenait à moi, j'ai pensé à un nombre possible 5 qui serait de prendre le contenu de la boucle while principale et d'en faire une méthode, et de résoudre ceci de manière récursive sans utiliser l'itérateur, et espérer éviter l'exception merci pour toute l'aide^_^ – Gwilym

+0

+1. La première phrase est la plus importante ... – Jared

0

De l'javadoc pour ArrayList:

Les itérateurs retourné par les méthodes iterator et ListIterator de cette classe sont fail-rapide: si la liste est structurellement modifiée à tout moment après l'itérateur est créé, de toute façon, sauf Grâce aux méthodes remove ou add de l'itérateur, l'itérateur lance une exception ConcurrentModificationException. Ainsi, face à une modification simultanée, l'itérateur échoue rapidement et proprement, plutôt que de risquer un comportement arbitraire et non déterministe à un moment indéterminé dans le futur.

1

boucle à l'intérieur Iterator vous essayez de modifier la liste des
Extrait de votre code

if (begin) { 
        // one of these is causing the problem 
        tour.add(0,C); 
       } 
       else { 
        // one of these is causing the problem 
        tour.add(C); 
       } 

Ce n'est pas admissible. Conforme à Javadoc http://java.sun.com/j2se/1.4.2/docs/api/java/util/ConcurrentModificationException.html

Cette exception peut être levée par méthodes qui ont détecté en même temps modification d'un objet lorsque cette modification n'est pas permis. Pour par exemple, il n'est généralement pas permssible pour un thread pour modifier une collection tandis qu'un autre thread est en itérant par dessus. En général, les résultats de l'itération ne sont pas définis dans ces circonstances. Certaines implémentations Iterator (y compris ceux de toutes les implémentations de la collection fournis par le JRE) peuvent choisir de jeter cette exception si ce comportement est détecté. Itérateurs qui font cela sont connus comme échec rapide itérateurs, car ils ne parviennent pas rapidement et proprement , plutôt que de risquer arbitraire, comportement non déterministe à un moment non déterminé à l'avenir.

1

Vous pouvez facilement contourner ce problème en utilisant une variable d'index au lieu d'un iterator:

int i = 0; 
while (i < tour.size()) { 
    .... 
    i++; 
} 

Cependant, vous remarquerez que l'insertion d'éléments dans une liste que vous itérez soulève des questions délicates. Il existe une raison pour laquelle Iterator lève une exception ConcurrentModificationException, à savoir que la logique de poursuite de l'itération n'est pas bien définie. Si vous insérez un élément avant votre position d'index, l'index ne pointe plus vers le même élément 'current' et vous devez augmenter l'index de deux pour trouver l'élément suivant. Si vous insérez après, alors rien ne change à l'exception de la condition d'arrêt (mais tour.size() va grandir correctement, donc c'est bon). Si vous faites plusieurs insertions/suppressions à différentes positions, il devient difficile de suivre ...

J'ai l'impression que votre algorithme peut aussi être simplifié, même si je ne comprends pas très bien ce qu'il est censé faire. .

+0

travailler quoi faire avec l'index tout en insérant des trucs a été très intresting^_ ^, mon code fonctionne maintenant et très bien (obtient dans 12% d'une solution optimale en environ 10 secondes) mais est très grand et laid son un algorithme très chancelant, mais je vais avec ce que le programmeur je respecte beaucoup m'a appris une fois, "si vous ne savez pas comment le coder bien le code mal puis l'améliorer petit à petit, jusqu'à ce que vous l'avez bien codé" – Gwilym

0

Ceci est mon code laid, mais travaille maintenant pour tous ceux qui veulent voir comment je l'ai eu à la fin, je compte plutôt il un peu, mais pour un moment im heureux avec le fait qu'il fonctionne

public void construct() 
{ 
tour = new ArrayList(); 
ArrayList<City> lcl = new ArrayList(l.getCitys()); 

tour.add(lcl.remove(0)); 
tour.add(lcl.remove(1)); 
tour.add(lcl.remove(2)); 

while (!this.tourComplete()) 
{ 

System.out.println(tour.size()); 
ListIterator<City> tourit = tour.listIterator(); 
City g1 = (City) tourit.next(); 
City g2 = (City) tour.get(tour.indexOf(g1)+1); 

int gapDist = l.distanceBetweenCitys(g1, g2); 

    while ((tourit.nextIndex()!=tour.size()-1) && !this.tourComplete()) 
    { 
    System.out.println("x"); 
    City C = null; 
    int best = Integer.MAX_VALUE; 

     for (ListIterator<City> lclit = lcl.listIterator(); lclit.hasNext();) 
     { 
     City c = (City) lclit.next(); 
     int avg = (l.distanceBetweenCitys(g1,c)+ l.distanceBetweenCitys(g2, c))/2 ; 

     if ((avg<gapDist) && (avg<best)) 
     { 
     C=c; 
     best=avg; 
     } 

     } 

    if (C==null) 
    { 
    System.out.println("C null"); 
     assert(best==Integer.MAX_VALUE); 
     City A = tour.get(0); 
     City Z = tour.get(tour.size()-1); 

     boolean begin = true; 

     for (ListIterator<City> lclit = lcl.listIterator(); lclit.hasNext();) 
     { 
      City c = (City) lclit.next(); 
      int dist = l.distanceBetweenCitys(A,c); 

      if (dist<best) 
      { 
      begin=true; 
      C=c; 
      best=dist; 
      } 

     } 

     for (ListIterator<City> lclit = lcl.listIterator(); lclit.hasNext();) 
     { 
      City c = (City) lclit.next(); 
      int dist = l.distanceBetweenCitys(Z,c); 

      if (dist<best) 
      { 
      begin=false; 
      C=c; 
      best=dist; 
      } 

     } 

     if(begin) 
     { 
     System.out.println("add at begining"); 
     System.out.println(this.TourtoString()); 
     // add in at 0 

      int itpos = tourit.nextIndex(); 
      //move iterator to 0 
      while (tourit.hasPrevious()) 
      { 
      tourit.previous(); 
      } 

      lcl.remove(C); 
      // add in C 
      tourit.add(C); 

      // move iterator back 
      while(tourit.nextIndex()!=(itpos+1)) 
      { 
      tourit.next(); 
      } 
     System.out.println(this.TourtoString()); 
     } 
     else 
     { 
     // add in at end 
      int itpos = tourit.nextIndex(); 
      //move iterator to end 
      while (tourit.hasNext()) 
      { 
      tourit.next(); 
      } 

      lcl.remove(C); 
      // add in C 
      tourit.add(C); 

      // move iterator back 
      while(tourit.nextIndex()!=itpos) 
      { 
      tourit.previous(); 
      } 
     } 

    } 
    else 
    { 
    System.out.println("C not null"); 

    // add in at g2 
    int moveto = tour.indexOf(g2); 
    int itpos = tourit.nextIndex(); 

    System.out.println("add at: "+ moveto); 
    System.out.println(this.TourtoString()); 


    if (itpos>=moveto) 
    { 
    itpos++; 
    } 

    //move iterator to 0 
    while (tourit.hasPrevious()) 
    { 
    tourit.previous(); 
    } 

    // move iterator to insertion location 
    while (tourit.nextIndex()!=moveto) 
    { 
    tourit.next(); 
    } 

    lcl.remove(C); 
    // add in C 
    tourit.add(C); 

    //move iterator to 0 
    while (tourit.hasPrevious()) 
    { 
    tourit.previous(); 
    } 


    // move iterator back 
    while(tourit.nextIndex()!=itpos) 
    { 
    tourit.next(); 
    } 

    System.out.println(this.TourtoString()); 
    } 


    g1 = (City) tourit.next(); 
    g2 = (City) tour.get(tour.indexOf(g1)+1); 
    gapDist = l.distanceBetweenCitys(g1, g2); 
    } 

} 

} 
Questions connexes