Java: исключение члена данных из метода equals - PullRequest
1 голос
/ 14 марта 2010
public class GamePiece {
    public GamePiece(char cLetter, int nPointValue) {
        m_cLetter=cLetter;
        m_nPointValue=nPointValue;
        m_nTurnPlaced=0;    //has not been placed on game board yet.
    }

    public char GetLetter() {return m_cLetter;}
    public int GetPointValue() {return m_nPointValue;}
    public int GetTurnPlaced() {return m_nTurnPlaced;}

    public void SetTurnPlaced(int nTurnPlaced) { m_nTurnPlaced=nTurnPlaced; }

    @Override
    public boolean equals(Object obj) {
        /*NOTE to keep this shorter I omitted some of the null checking and instanceof stuff. */
        GamePiece other = (GamePiece) obj;

        //not case sensitive, and I don`t think we want it to be here.
        if(m_cLetter != other.m_cLetter) {
            return false;
        }

        if(m_nPointValue != other.m_nPointValue) {
            return false;
        }
        /* NOTICE! m_nPointValue purposely omitted.  It does not affect hashcode or equals */

        return true;
    }

    @Override public int hashCode() {
        /* NOTICE! m_nPointValue purposely omitted.  It should not affect hashcode or equals */
        final int prime = 41;
        return prime * (prime + m_nPointValue + m_cLetter);
    }

    private char m_cLetter;
    private int m_nPointValue;
    private int m_nTurnPlaced;//turn which the game piece was placed on the game board.  Does not affect equals or has code!
}

Рассмотрим данный фрагмент кода.Этот объект был неизменным до появления члена m_nTurnPlaced (который может быть изменен методом SetTurnPlaced, поэтому теперь GamePiece становится изменяемым).

GamePiece используется в ArrayList, я вызываю методы метода remove и remove, которые оба полагаются на метод equals, который будет реализован.некоторые члены не влияют на равенства и хэш-код?Как это повлияет на его использование в моем ArrayList?Какой тип коллекций java НЕ будет безопасным для использования этого объекта сейчас, когда он изменчив?Мне сказали, что вы не должны переопределять equals для изменяемых объектов, потому что это приводит к тому, что некоторые коллекции ведут себя «странно» (я читал это где-то в документации Java).

Ответы [ 4 ]

2 голосов
/ 14 марта 2010

Да, конечно, как вы определяете свои equals и hashCode, и как / когда вы изменяете объекты, это может привести к тому, что коллекции будут вести себя "странно". Вот пример:

BitSet bitset = new BitSet();
Collection<BitSet> bitsetcol = new HashSet<BitSet>();
bitsetcol.add(bitset);
System.out.println(bitsetcol.contains(bitset)); // prints true
bitset.set(42);
System.out.println(bitsetcol.contains(bitset)); // prints false!!!

В данном случае Bitset определяет equals и hashCode в зависимости от того, какие биты установлены. HashSet находит объекты, используя hashCode и equals. Изменив bitset, теперь у него есть другой hashCode, и поэтому bitsetcol больше не может его найти.

Вы заметите, что если bitsetcol = new ArrayList<BitSet>();, то он все равно может его найти! Различные реализации Collection имеют разные уровни толерантности к такому механизму мутаций.


Что касается @Override equals для изменчивого типа, да, это, конечно, хорошо. BitSet конечно, это делает. Самое главное, это именно то поведение, которое вы ожидаете от BitSet (который предназначен для изменчивости по соображениям производительности).

Если для вашего изменяемого класса имеет смысл @Override equals и hashCode, тогда сделайте это.

1 голос
/ 14 марта 2010

Примечание: этот ответ касается вообще изменяемых объектов, то есть объектов, для которых методы hashCode и equals зависят от значения изменяемых членов. В вашем случае, GamePiece является изменяемым, но методы equals и hashCode не зависят от изменяемых членов.

У меня такой вопрос, это нормально или обычная практика в Java для некоторых Члены не должны влиять на равных и хэш-код?

Это приемлемая практика, если одни и те же члены используются для вычисления равных и хэш-кода.

Как это повлияет на его использование в моем ArrayList?

Как правило, изменяемые объекты не влияют на использование ArrayList: вы можете вставлять изменяемые объекты, добавлять (объект) , получать доступ к изменяемым объектам, используя их индекс, get (index) и удалить изменяемые объекты, используя их индекс remove (index) , без каких-либо побочных эффектов, поскольку ArrayList не вызывает equals () для этих методов.

Если вы удаляете или ищите объект с помощью методов remove (object) и indexOf (object), ArrayList вызовет equals () для объектов в ArrayList, поэтому вашей программе необходимо знать, что объекты в ArrayList возможно, изменился, так как они были вставлены. Это не будет проблемой для GamePiece, так как метод equals () не зависит от изменяемого члена.

Какой тип коллекций java будет НЕ безопасно использовать этот объект сейчас что это изменчиво?

Просто проверьте API коллекций , чтобы узнать, на какие классы или методы влияют изменяемые объекты. В общем случае не следует использовать изменяемые объекты в качестве ключа карты, но поскольку изменяемый член не влияет на методы hashCode и equals в GamePiece, вы можете использовать его в качестве ключа.

"Примечание: следует проявлять особую осторожность, если в качестве ключей карты используются изменяемые объекты. Поведение карты не указывается, если значение объекта изменяется таким образом, что это влияет на сопоставления равных, когда объект ключ на карте. Особый случай этого запрета состоит в том, что недопустимо, чтобы карта содержала себя в качестве ключа. В то время как для карты допустимо содержать себя в качестве значения, рекомендуется соблюдать крайнюю осторожность: Методы hashCode больше не определены на такой карте. "

1 голос
/ 14 марта 2010

Равенство имеет значение, которое вы хотите придать ему ..

В вашем случае вы явно имеете в виду, что экземпляр GamePiece с разными m_nTurnPlaced будет рассматриваться и рассматриваться как равный объект. Таким образом, m_nTurnPlaced является состоянием этого объекта, и его не следует рассматривать на equals(o) и hashcode().

Что это значит?

  • прежде всего хеш-код GamePiece не изменится, когда вы измените m_nTurnPlaced, и это хорошо! В противном случае вы не сможете использовать хэш-карты или хэш-наборы,
  • когда вы будете использовать добавление или удаление в списках, вы правильно рассмотрите GameState: это фактически означает, что вы не можете различить два GameStates с разными значениями m_nTurnPlaced ..
1 голос
/ 14 марта 2010

Часть красоты в том, что вы можете свободно проектировать свои .equals() и .hashcode() так, как того требует ваш объект.

Определенные типы коллекций очень зависят от .equals() и .hashcode(), ведущих себя очень последовательно . В этой статье объясняется, как переопределить эти методы, используя лучшие практики .

...