views:

121

answers:

4
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!
}

Consider the given piece of code. This object has been immutable until the introduction of the m_nTurnPlaced member (which can be modified by the SetTurnPlaced method, so now GamePiece becomes mutable).

GamePiece is used in an ArrayList, I call contains and remove methods which both rely on the equals method to be implemented.

My question is this, is it ok or common practice in Java for some members to not affect equals and hashcode? How will this affect its use in my ArrayList? What type of java Collections would it NOT be safe to use this object now that it is mutable? I've been told that you're not supposed to override equals on mutable objects because it causes some collections to behave "strangely" (I read that somewhere in the java documentation).

+1  A: 

Part of the beauty is that you're able to freely design your .equals() and .hashcode() as your object necessitates.

Certain types of collections are very dependent on .equals() and .hashcode() behaving very consistently. This article explains how to override these methods using best practices.

Dolph
+1  A: 

Equality has the meaning that you want to give to it..

In your case you explictly mean that instance of GamePiece that have different m_nTurnPlaced will be considered and treated as equal objects. So m_nTurnPlaced is a state of this object and it shouldn't be considered on equals(o) and hashcode().

What does it mean?

  • first of all hashcode of GamePiece won't change when you change m_nTurnPlaced and this is good! Otherwise you couldn't use hashmaps or hashsets,
  • when you'll use add or remove on lists you will comsider the GameState correctly: this actually implies that you can't distinguish two GameStates with different m_nTurnPlaced values..
Jack
+2  A: 

Yes, certainly how you define your equals and hashCode, and how/when you mutate objects, can cause collections to behave "strangely". Here's an example:

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!!!

What happens here is that Bitset defines equals and hashCode in terms of which bits are set. HashSet finds objects using hashCode and equals. By modifying bitset, it now has a different hashCode, and therefore bitsetcol could no longer find it.

You'll notice that if bitsetcol = new ArrayList<BitSet>();, then it could still find it! Different Collection implementations have different levels of tolerance to this kind of mutation mechanism.


As for can you @Override equals on a mutable type, yes, that is certainly fine. BitSet certainly does that. Most importantly, that's exactly the behavior that you'd expect from a BitSet (which is designed to be mutable for performance reason).

If it makes sense for your mutable class to @Override equals and hashCode, then do so.

polygenelubricants
If I know I'll never use this GamePiece as a key in a hash based collection i'll be ok?
cchampion
Is it ok to override equals on a mutable object?
cchampion
There's no guarantee. You have to read the documentation and confirm that that particular collection implementation tolerates mutation to objects that it contains that may change its `equals` or `hashCode`.
polygenelubricants
Yes, you can override equals on mutable object. That's what `BitSet` does.
polygenelubricants
+1  A: 

Note: this answer is about mutable objects in general, i.e., objects for which the hashCode and equals methods depend on the valus of mutable members. In your case, GamePiece is mutable, but the equals and hashCode methods are not affected by mutable members.

My question is this, is it ok or common practice in Java for some members to not affect equals and hashcode?

This is an acceptable practice, as long as the same members are used to compute equals and hashcode.

How will this affect its use in my ArrayList?

In general, mutable objects won't affect your use of ArrayList: you can insert mutable objects, add(object), access mutable objects using their index, get(index), and delete mutable objects using their index, remove(index), without any side effect because ArrayList does not call equals() for these methods.

If you remove or search for an object using the remove(object) and indexOf(object) methods, ArrayList will call equals() on the objects in the ArrayList, so your program needs to be aware that the objects in the ArrayList might have changed since they were inserted. This won't be a problem with GamePiece as the equals() method is not dependent on the mutable member.

What type of java Collections would it NOT be safe to use this object now that it is mutable?

Just check the Collections API to learn what classes or methods are affected by mutable objects. In general, you should not use mutable objects as Map key, but because the hashCode and equals methods of GamePiece are not affected by the mutable member, you can use it as a key.

"Note: great care must be exercised if mutable objects are used as map keys. The behavior of a map is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is a key in the map. A special case of this prohibition is that it is not permissible for a map to contain itself as a key. While it is permissible for a map to contain itself as a value, extreme caution is advised: the equals and hashCode methods are no longer well defined on such a map."

Barthelemy
He can use it as a map key alright: /* NOTICE! m_nPointValue purposely omitted. It should not affect hashcode or equals */
FredOverflow
@FredOverflow, thanks, I edited my answer to clarify this.
Barthelemy