Reputation: 7921
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).
Upvotes: 1
Views: 399
Reputation: 8757
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."
Upvotes: 1
Reputation: 383756
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.
Upvotes: 2
Reputation: 133587
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?
GamePiece
won't change when you change m_nTurnPlaced
and this is good! Otherwise you couldn't use hashmaps or hashsets,GameState
correctly: this actually implies that you can't distinguish two GameStates
with different m_nTurnPlaced
values..Upvotes: 1
Reputation: 50650
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.
Upvotes: 1