Почему мой HashMap распознает ключи хеша, которые еще не должны быть ключами? - PullRequest
1 голос
/ 23 марта 2011

Я программирую игру Отелло и сохраняю возможные ходы, найденные в HashMap, следующим образом:

Hashmap<Matrix, PossibleMovesVector>

Матрица - это объект, содержащий int[8][8], которыйописывает текущую ситуацию на доске.Он переопределяет hashCode() и equals() следующим образом.(Это было необходимо, потому что стандарт hashCode() для многомерных массивов не смотрит на содержимое вложенных массивов.)

@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + Arrays.deepHashCode(board);
    return result;
}

@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    Matrix other = (Matrix) obj;
    if (!Arrays.equals(board, other.board))
        return false;
    return true;
}

Это, похоже, работает нормально.Но когда я проверил это и нарисовал около 100 или около того новых плат на моем экране, я внезапно получил следующее сообщение в моей консоли:

Распознанный hashCode xxxxxxxxxx
Получение ходов из HashMap ...

Но это происходит для доски, которая, как я знаю, нова.Может ли быть так, что у Отелло так много состояний, что хэш-коды повторяются?Это единственное объяснение, которое я смог найти.Это или что-то не так в моем коде.Но я не думаю, что в последнем сценарии дело обстоит именно так, потому что для того, чтобы пойти не так, нужно так много времени.

РЕДАКТИРОВАТЬ: Примерно так работает моя программа.getMoves () немного многословно, но все, что он делает: проверяет, проверена ли уже доска на ходы, если нет, проверяет возможные ходы.
- всякий раз, когда игрок делает ход, меняется m.board.(Вместо создания совершенно нового объекта Matrix.)
- boardHash содержит еще одну хэш-таблицу (хотя я бы предпочел простой и простой массив), в которой хранится информация о том, возможен ли ход для игрока 1 или игрока 2. В моем предыдущем постеЯ не думал, что это актуально, поэтому упростил это до одного вектора.

Код, примерно, выглядит так:

public void Init(){  
    //Initialize board  
    m_board = new Matrix(new int[8][8]);  
    m_board.Init();  
    //Compute initial possible moves  
    boardHash.put(m_board, new HashMap<Integer, Vector<Matrix>>());  
    boardHash.get(m_board).put(whosTurn, getPossibleMoves(whosTurn));  
}   

    public Vector<Matrix> getPossibleMoves(int forWho) {
        // Assign variable so the code doesn't get too cluttered
        HashMap<Integer, Vector<Matrix>> moveMap = boardHash.get(m_board);

        // Maybe moveMap doesn't even exist is our lookup table yet
        if(moveMap == null){
            System.out.println("\nNever before seen board...");
            moveMap = new HashMap<Integer, Vector<Matrix>>();
            boardHash.put(m_board, moveMap);
            moveMap.put(forWho,getPossibleMoves(forWho));
        }else{
            System.out.println("\nRecognized hashCode "+m_board.hashCode());                
            // If it does exist, maybe no possible moves are stored in it 
            if(moveMap.get(forWho)==null){  
                boardHash.put(m_board, m_board.board);
                //So then make it:
                // System.out.println("No moves for "+side+" yet...");
                Vector<Matrix> v_possMoves = new Vector<Matrix>();
                //For every empty square on the current field...
                for(int column = 0; column<8; column++){
                    for(int row = 0; row<m_board.board[column].length; row++){
                        if(m_board.board[column][row] == 0){
                            //Check if this move is valid, and if it is, return the resulting board
                            Matrix possibleMoveMatrix = new Matrix(makeLines(column,row,forWho));
                            if(possibleMoveMatrix.board != null){
                                //add it to the vector
                                v_possMoves.add(possibleMoveMatrix);
                            }
                        }
                    }
                }
                if(v_possMoves.isEmpty()){
                    System.out.println("No possible moves for player "+forWho);
                }
                moveMap.put(forWho,v_possMoves);
            }
        }
        return moveMap.get(forWho);
        }

Ответы [ 2 ]

14 голосов
/ 23 марта 2011

РЕДАКТИРОВАТЬ: Я только что подумал о чем-то, что может , безусловно, вызвать проблемы.Вы когда-нибудь изменяли массив в Matrix?Вы не должны изменять соответствующие значения в ключе после добавления его на карту - это может привести к ложным срабатываниям и даже ложным отрицаниям (вы можете использовать ту же ссылку на ключ, которую вы использовали в put, но ненайти совпадение с get, если вы внесли изменение, которое влияет, например, на хеш-код).


Не ясно, какой код печатает часть "Распознанный хэш-код" ... ноПохоже, что это при условии, что хэш-коды являются уникальными. Не делайте этого. Они не должны быть уникальными.Они предназначены для указания возможного равенства - оптимизации, позволяющей быстрее находить что-то.Идея состоит в том, что если вы найдете соответствующий хеш-код, вы должны всегда проверять "реальное" равенство.

Вы не сказали, что представляют собой значения в вашем массиве, но предполагая, что они0 (пусто) 1 (черный) и 2 (белый) есть далеко больше возможных комбинаций, чем может представлять простой 32-битный хэш-код ... у вас есть 3 64 возможностей, что превышает 2 32 .Теперь я осмелюсь сказать, что большинство из них не являются реально осуществимыми платами Отелло, но я был бы удивлен, если бы Arrays.deepHashCode попытался оптимизировать именно для этого случая, чтобы предоставить уникальные хэш-коды для всех допустимых плат Отелло, даже если это возможно возможно.

Как примечание: ваш hashCode метод является более сложным, чем это необходимо.Попробуйте это:

@Override
public int hashCode() { 
  return Arrays.deepHashCode(board); 
}
1 голос
/ 24 марта 2011

Использование Arrays.equals в вашей реализации equals() не правильно. Вы должны использовать Arrays.deepEquals. Таким образом, ваша реализация equals () закончится:

Matrix other = (Matrix) obj; return Arrays.deepEquals(board, other.board);

См. http://download.oracle.com/javase/6/docs/api/java/util/Arrays.html

...