Search code examples
javahashsetconcurrentmodification

Sudoku solver with Exception in thread "main" java.util.ConcurrentModificationException on its Iterator


I'm getting the following errors:

     Exception in thread "main" java.util.ConcurrentModificationException 
        at java.util.HashMap$HashIterator.nextEntry(HashMap.java:810)
        at java.util.HashMap$KeyIterator.next(HashMap.java:845)
        at sudoku.Main.solve2(Main.java:143)
        at sudoku.Main.next2(Main.java:168)
        at sudoku.Main.solve2(Main.java:153)
        at sudoku.Main.main(Main.java:284) 

I don't understand the java.util.HashMap$KeyIterator.next and java.util.HashMap$HashIterator.nextEntry error messages, as I'm not able to get the keySet for a HashSet explicitly I assumed the Iterator was going through the keySet by default.

I'm not using threads, just recursive calls. What's going on here?

 static void solve2(int row, int col, int [][]grid,  ArrayList<HashSet<Integer>> availableNumsInRows,
          ArrayList<HashSet<Integer>> availableNumsInColumns){

     if (row>=grid.length){

           System.out.println("solution found");
            printSolvedGrid(grid);

            System.out.println("move count for this sudoku is " + moveCounter);
            moveCounter=0; //reset counter
           return;


       }

       if( grid[row][col] != 0 ){
            next2( row, col, grid, availableNumsInRows, availableNumsInColumns ) ;
       }

       else {
         // Find a valid number for the empty cell

         Iterator <Integer> iterator = availableNumsInRows.get(row).iterator();


         for( int num = iterator.next() ; iterator.hasNext(); num = iterator.next())
         {
            if( checkRow(row,num,grid) && checkCol(col,num,grid) && checkBox(row,col,num,grid) )
            {
               grid[row][col] = num ;
               availableNumsInRows.get(row).remove(new Integer(num));
               availableNumsInColumns.get(col).remove(new Integer(num));
               moveCounter++;

               //printSolvedGrid(grid);
               next2( row, col, grid, availableNumsInRows, availableNumsInColumns );

            }
         }

         grid[row][col] = 0 ;
       }

  }

  //helper function for the first solution approach
  public static void next2( int row, int col, int [][] grid ,  ArrayList<HashSet<Integer>> availableNumsInRows,
          ArrayList<HashSet<Integer>> availableNumsInColumns )
   {
      if( col < 8 ) //pass to next col
         solve2( row, col + 1, grid, availableNumsInRows, availableNumsInColumns) ;
      else //pass to next row
         solve2( row + 1, 0, grid, availableNumsInRows, availableNumsInColumns) ;
   }

Edit:

I changed the code to:

   while (iterator.hasNext())
             {

                num=iterator.next();

                if( checkRow(row,num,grid) && checkCol(col,num,grid) && checkBox(row,col,num,grid) )
                {
                   grid[row][col] = num ;

                   iterator.remove();

                   moveCounter++;


                   next2( row, col, grid, availableNumsInRows, availableNumsInColumns );

                }

             }

and I'm still getting the ConcurrentModificationException, why is this?

Exception in thread "main" java.util.ConcurrentModificationException
        at java.util.HashMap$HashIterator.nextEntry(HashMap.java:810)
        at java.util.HashMap$KeyIterator.next(HashMap.java:845)
        at sudoku.Main.solve2(Main.java:148)
        at sudoku.Main.next2(Main.java:175)
        at sudoku.Main.solve2(Main.java:137)
        at sudoku.Main.next2(Main.java:175)
        at sudoku.Main.solve2(Main.java:159)
        at sudoku.Main.next2(Main.java:175)
        at sudoku.Main.solve2(Main.java:137)
        at sudoku.Main.next2(Main.java:175)
        at sudoku.Main.solve2(Main.java:159)
        at sudoku.Main.next2(Main.java:175)
        at sudoku.Main.solve2(Main.java:159)
        at sudoku.Main.main(Main.java:291)
Java Result: 1

Solution

  • You're iterating over the hash map but also modifying it in the same loop. That will cause exactly this exception.

    You can avoid this within one call by calling iterator.remove() instead of availableNumsInRows.get(row).remove(new Integer(num));

    However, you're recursing, creating a new iterator each time. If you remove something via one of the iterators in the nested call, then when you come to iterate in the outer call, you'll have the same problem.

    One option would be to simplify the code to avoid recursing in this way; another would be to use a single iterator and pass that around.