Java:从 equals 方法中省略数据成员
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中,我调用了contains和remove方法,它们都依赖于equals方法来实现。
我的问题是,Java 中某些成员不影响 equals 和 hashcode 是否可以或者是常见的做法?这将如何影响它在我的 ArrayList 中的使用?既然这个对象是可变的,那么使用什么类型的 java 集合是不安全的?有人告诉我,你不应该覆盖可变对象上的 equals,因为它会导致某些集合表现得“奇怪”(我在 java 文档中的某个地方读到了这一点)。
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).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
是的,当然,您如何定义
equals
和hashCode
,以及如何/何时改变对象,可能会导致集合表现“奇怪”。下面是一个示例:这里发生的情况是,
Bitset
根据设置的位定义了equals
和hashCode
。HashSet
使用hashCode
和equals
查找对象。通过修改bitset
,它现在具有不同的hashCode
,因此bitsetcol
无法再找到它。您会注意到,如果
bitsetcol = new ArrayList();
,那么它仍然可以找到它!不同的Collection
实现对这种变异机制有不同程度的容忍度。至于你可以在可变类型上使用
@Override equals
吗?是的,这当然没问题。BitSet
确实做到了这一点。最重要的是,这正是您所期望的 BitSet 行为(出于性能原因,它被设计为可变的)。如果您的可变类对
@Override equals
和hashCode
有意义,那么就这样做。Yes, certainly how you define your
equals
andhashCode
, and how/when you mutate objects, can cause collections to behave "strangely". Here's an example:What happens here is that
Bitset
definesequals
andhashCode
in terms of which bits are set.HashSet
finds objects usinghashCode
andequals
. By modifyingbitset
, it now has a differenthashCode
, and thereforebitsetcol
could no longer find it.You'll notice that if
bitsetcol = new ArrayList<BitSet>();
, then it could still find it! DifferentCollection
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 aBitSet
(which is designed to be mutable for performance reason).If it makes sense for your mutable class to
@Override equals
andhashCode
, then do so.注意:这个答案是关于一般的可变对象,即 hashCode 和 equals 方法依赖于可变成员的值的对象。在您的情况下,GamePiece 是可变的,但 equals 和 hashCode 方法不受可变成员的影响。
这是一种可以接受的做法,只要使用相同的成员来计算 equals 和 hashcode。
一般来说,可变对象不会影响您对 ArrayList 的使用:您可以插入可变对象,add(object),使用其索引访问可变对象,get(index) ,并使用其索引删除可变对象,remove(index),没有任何副作用,因为 ArrayList 不会为这些方法调用 equals()。
如果您使用remove(object)和indexOf(object)方法删除或搜索对象,ArrayList将对ArrayList中的对象调用equals(),因此您的程序需要意识到ArrayList中的对象可能已更改自从它们被插入以来。这对于 GamePiece 来说不会是问题,因为 equals() 方法不依赖于可变成员。
只需检查 Collections API 了解什么类或方法受可变对象的影响。一般来说,你不应该使用可变对象作为Map key,但是因为GamePiece的hashCode和equals方法不受可变成员的影响,所以你可以使用它作为key。
“注意:如果将可变对象用作映射键,则必须格外小心。如果对象的值以影响等于比较的方式更改,而该对象是映射中的键,则不会指定映射的行为此禁止的一个特殊情况是不允许映射将自身包含为键,而允许映射将自身包含为值,但建议格外小心:equals 和 hashCode 方法是。在这样的地图上不再有明确的定义。”
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.
This is an acceptable practice, as long as the same members are used to compute equals and hashcode.
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.
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."
其优点之一是您可以根据对象的需要自由设计
.equals()
和.hashcode()
。某些类型的集合非常依赖
.equals()
和.hashcode()
的行为非常一致。 本文介绍如何使用最佳实践覆盖这些方法。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.平等具有您想要赋予它的含义。
在您的情况下,您明确表示具有不同
m_nTurnPlaced
的GamePiece
实例将被视为相同的对象。因此,m_nTurnPlaced
是该对象的一种状态,不应在equals(o)
和hashcode()
上考虑它。这是什么意思?
m_nTurnPlaced
时,GamePiece
的哈希码不会更改,这很好!否则,您无法使用哈希图或哈希集,GameState
:这实际上意味着您无法区分两个GameState
code> 具有不同的m_nTurnPlaced
值。Equality has the meaning that you want to give to it..
In your case you explictly mean that instance of
GamePiece
that have differentm_nTurnPlaced
will be considered and treated as equal objects. Som_nTurnPlaced
is a state of this object and it shouldn't be considered onequals(o)
andhashcode()
.What does it mean?
GamePiece
won't change when you changem_nTurnPlaced
and this is good! Otherwise you couldn't use hashmaps or hashsets,GameState
correctly: this actually implies that you can't distinguish twoGameStates
with differentm_nTurnPlaced
values..