[英]How to override equals(), hashcode() and compareTo() for a HashSet
I am trying to override the mentioned methods for my HashSet
: 我试图覆盖我的HashSet
提到的方法:
Set<MyObject> myObjectSet = new HashSet<MyObject>();
MyObject: 为MyObject:
public class MyObject implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
String name;
int number;
Map<String,String> myMap;
public MyObject(String name, int number, Map<String,String> myMap) {
this.name = name;
this.number = number;
this.myMap = myMap;
}
[...]
}
How do I override the hashcode(), equals() and compareTo() method? 如何覆盖hashcode(),equals()和compareTo()方法?
Currently I have the following: 目前我有以下内容:
public int hashCode () {
return id.hashCode();
}
// override the equals method.
public boolean equals(MyObject s) {
return id.equals(s.id);
}
// override compareTo
public int compareTo(MyObject s) {
return id.compareTo(s.id);
}
I read that comparing by id is not enough this is object is a persistent entity for the DB (see here ). 我读到,通过id进行比较是不够的,这是对象是DB的持久实体(参见此处 )。
The name and number aren't unique across all objects of this type. 此类型的所有对象的名称和编号都不是唯一的。
So how should I override it? 那我应该怎么覆盖呢?
Do I also need to compare the hashMap inside it? 我还需要比较里面的hashMap吗?
I am confused. 我很迷惑。 The only unique thing about the object is the the map myMap which gets populated later in the lifecycle. 关于该对象的唯一独特之处是地图myMap将在生命周期的后期填充。
How do I check for its equality? 我如何检查其相等性?
Based on all the responses I have changed the methods to the following 基于所有响应,我已将方法更改为以下内容
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final MyComplexObj myComplexObj = (MyComplexObj) o;
return myMap != null ? myMap.equals(myComplexObj.myMap) : myComplexObj.myMap == null;
}
@Override
public int hashCode() {
return myMap != null ? myMap.hashCode() : 0;
}
public int compareTo(MyComplexObj o) {
return myMap.compareTo(o.getMyMap()));
}
This fails at the compareTo method, "this method is undefined for the type Map 这在compareTo方法中失败,“对于类型Map,此方法未定义
This is what intellij default option gives 这是intellij默认选项提供的内容
import java.util.Map;
public class MyObject {
String name;
int number;
Map<String,String> myMap;
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final MyObject myObject = (MyObject) o;
if (number != myObject.number) return false;
if (name != null ? !name.equals(myObject.name) : myObject.name != null) return false;
return myMap != null ? myMap.equals(myObject.myMap) : myObject.myMap == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + number;
result = 31 * result + (myMap != null ? myMap.hashCode() : 0);
return result;
}
}
But, since you said 但是,既然你说过
The only unique thing about the object is the the map myMap which gets populated later in the lifecycle. 关于该对象的唯一独特之处是地图myMap将在生命周期的后期填充。
I would just keep myMap and skip both name and number (But this begs the question, why would you include a redundant data- name and number in all the elements of your collection?) 我会保留myMap并跳过名称和号码(但是这引出了一个问题,为什么你会在你的集合的所有元素中包含冗余数据名和数字?)
Then it becomes 然后它变成了
import java.util.Map;
public class MyObject {
String name;
int number;
Map<String,String> myMap;
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final MyObject myObject = (MyObject) o;
return myMap != null ? myMap.equals(myObject.myMap) : myObject.myMap == null;
}
@Override
public int hashCode() {
return myMap != null ? myMap.hashCode() : 0;
}
}
Keep in mind that, there are other ways too for the equals and hashcode methods. 请记住,equals和hashcode方法还有其他方法。 For example, Here are the options that intelliJ gives for code generation 例如,以下是intelliJ为代码生成提供的选项
Unlike Equals and Hashcode, here is no contract exist between compareTo and any other behaviors. 与Equals和Hashcode不同,compareTo和任何其他行为之间不存在契约。 You don't really need to do anything with compareTo until you want to make use of it for say, sorting. 你不需要对compareTo做任何事情,直到你想利用它来进行排序。 To read more about CompareTo Why should a Java class implement comparable? 阅读有关CompareTo的更多信息为什么Java类应该具有可比性?
The basic question here is "How can you determine if two objects are equal to each other?" 这里的基本问题是“你怎么能确定两个物体是否相等?”
This is a simple question for simple objects. 对于简单对象,这是一个简单的问题。 However, it becomes increasingly difficult with even slightly more complex objects. 然而,即使是稍微复杂的物体也变得越来越困难。
As stated in the original question: 如原始问题所述:
The only unique thing about the object is the the map myMap which gets populated later in the lifecycle. 关于该对象的唯一独特之处是地图myMap将在生命周期的后期填充。
Given two instances of the type MyObject
, the member variables myMap
must be compared with each other. 给定两个MyObject
类型的实例,必须将成员变量myMap
相互比较。 This map is of type Map<String, String>
. 此映射的类型为Map<String, String>
。 A few questions immediately come to mind: 立刻想到几个问题:
Answers to each of these questions will vary between applications. 每个问题的答案因应用程序而异。 In order to keep this applicable to a general audience, the following assumptions are being made: 为了使其适用于一般受众,正在做出以下假设:
The beauty of using equals()
, hashCode()
, and compareTo()
is that once hashCode()
is implemented properly, the other functions can be defined based on hashCode()
. 使用equals()
, hashCode()
和compareTo()
是,一旦hashCode()
被正确实现,其他函数可以基于hashCode()
来定义。
Considering all of that, we have the following implementation: 考虑到所有这些,我们有以下实现:
@Override
public boolean equals(final Object o)
{
if (o instanceof MyObject)
{
return (0 == this.compareTo(((MyObject) o)));
}
return false;
}
@Override
public int hashCode()
{
return getKeyValuePairs(this.myMap).hashCode();
}
// Return a negative integer, zero, or a positive integer
// if this object is less than, equal to, or greater than the other object
public int compareTo(final MyObject o)
{
return this.hashCode() - o.hashCode();
}
// The Map is flattened into a single String for comparison
private static String getKeyValuePairs(final Map<String, String> m)
{
final StringBuilder kvPairs = new StringBuilder();
final String kvSeparator = "=";
final String liSeparator = "^";
if (null != m)
{
final List<String> keys = new ArrayList<>(m.keySet());
Collections.sort(keys);
for (final String key : keys)
{
final String value = m.get(key);
kvPairs.append(liSeparator);
kvPairs.append(key);
kvPairs.append(kvSeparator);
kvPairs.append(null == value ? "" : value);
}
}
return 0 == kvPairs.length() ? "" : kvPairs.substring(liSeparator.length());
}
All the critical work is being done inside of hashCode()
. 所有关键工作都在hashCode()
内部完成。 For sorting, the compareTo()
function only needs to return a negative/zero/positive number -- a simple hashCode()
diff. 对于排序, compareTo()
函数只需要返回一个负数/零/正数 - 一个简单的hashCode()
差异。 And the equals()
function only needs to return true/false -- a simple check that compareTo()
equals zero. 而equals()
函数只需要返回true / false - compareTo()
等于零的简单检查。
For further reading, there is a famous dialogue by Lewis Carroll on the foundations of logic, which touches on the basic question of equality: 为了进一步阅读,路易斯·卡罗尔(Lewis Carroll)就逻辑基础进行了一次着名的对话,其中涉及平等的基本问题:
https://en.wikipedia.org/wiki/What_the_Tortoise_Said_to_Achilles https://en.wikipedia.org/wiki/What_the_Tortoise_Said_to_Achilles
And, in regard to even simple grammatical constructs, there is a fine example of two "equal" sentences at the start of chapter 6, "Pig and Pepper" , from Alice in Wonderland : 而且,就简单的语法结构而言,在爱丽丝梦游仙境的第6章“猪和胡椒”的开头有两个“平等”句子的优秀例子:
The Fish-Footman began by producing from under his arm a great letter, and this he handed over to the other, saying, in a solemn tone, "For the Duchess. An invitation from the Queen to play croquet." 鱼足者开始从他的胳膊下面写下一封很棒的信,然后他把它交给另一个人,用庄严的语调说:“公爵夫人。邀请女王来玩槌球。” The Frog-Footman repeated, in the same solemn tone, "From the Queen. An invitation for the Duchess to play croquet." 青蛙步兵以同样庄严的语调重复着“从女王那里。邀请公爵夫人去玩槌球。” Then they both bowed low and their curls got entangled together. 然后他们鞠躬低沉,卷曲纠缠在一起。
compareTo()
is relevant to sorting. compareTo()
与排序有关。 It has no relevance to a HashSet
or HashMap
. 它与HashSet
或HashMap
无关。
A properly working equals()
and hashCode()
are vital for members of hash-based collections. 正确工作的equals()
和hashCode()
对于基于散列的集合的成员至关重要。 Read their specifications in the Javadoc for Object
. 在Javadoc for Object
阅读它们的规范。
Possibly the definitive recommendations for implementing these are in Joshua Bloch's Effective Java . 实现这些的最终建议可能在Joshua Bloch的Effective Java中 。 I recommend reading the relevant chapter -- it's easily Google-able. 我建议阅读相关章节 - 它很容易谷歌。 There's no point in trying to paraphrase it all here. 试图在这里解释一切毫无意义。
One thing that may have escaped your notice, is that your field myMap
has a working equals()
and hashCode()
of its own, so you don't have to do anything special with it. 可能没有引起注意的一件事是你的字段myMap
有一个自己的equals()
和hashCode()
,所以你不必对它做任何特殊的事情。 If you can guarantee that none of the fields are null, a reasonable hashCode()
would be (following Bloch's system): 如果你可以保证没有字段为null,那么合理的hashCode()
将是(遵循Bloch的系统):
public int hashCode() {
int result = 44; // arbitrarily chosen
result = 31 * result + (int) (id ^ (id >>> 32));
result = 31 * result + name.hashCode();
result = 31 * result + number;
result = 31 * result + myMap.hashCode();
return result;
}
(You'll need more code if any of these could be null) (如果其中任何一个可能为null,您将需要更多代码)
Pretty much all IDEs will automatically generate both equals()
and hashcode()
, using all the fields in the class. 几乎所有IDE都会使用类中的所有字段自动生成equals()
和hashcode()
。 They'll use something very similar to Bloch's recommendations. 他们将使用与Bloch的建议非常相似的东西。 Hunt around the UI. 寻找用户界面。 You'll find it. 你会找到的。
Another alternative is to use Apache ReflectionUtils, which allows you to simply use: 另一种方法是使用Apache ReflectionUtils,它允许您简单地使用:
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
@Override
public boolean equals(final Object obj) {
return EqualsBuilder.reflectionEquals(this, obj);
}
This works out which fields to use at runtime, and applies Bloch's methods. 这解决了在运行时使用哪些字段,并应用Bloch的方法。
If you want to make myMap
implements comparable, and any other methods that you want, create decorator that implement comparable interface and delegate all other methods to enclosing myMap
instance. 如果你想使myMap
实现可比,以及你想要的任何其他方法,创建实现类似接口的装饰器并委托所有其他方法来封装myMap
实例。
public class ComparableMap implements Map<String, String>, Comparable<Map<String, String>> {
private final Map<String, String> map;
public ComparableMap(Map<String, String> map) {
this.map = map;
}
@Override
public int compareTo(Map<String, String> o) {
int result = 0;
//your implementation based on values on map on you consider one map bigger, less or as same as another
return result;
}
@Override
public boolean equals(Object obj) {
return map.equals(obj);
}
@Override
public int hashCode() {
return map.hashCode();
}
// map implementation methods
@Override
public int size() {
return map.size();
}
@Override
public boolean isEmpty() {
return map.isEmpty();
}
@Override
public boolean containsKey(Object key) {
return map.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
return map.containsValue(value);
}
@Override
public String get(Object key) {
return map.get(key);
}
@Override
public String put(String key, String value) {
return map.put(key, value);
}
@Override
public String remove(Object key) {
return map.remove(key);
}
@Override
public void putAll(Map<? extends String, ? extends String> m) {
map.putAll(m);
}
@Override
public void clear() {
map.clear();
}
@Override
public Set<String> keySet() {
return map.keySet();
}
@Override
public Collection<String> values() {
return map.values();
}
@Override
public Set<Entry<String, String>> entrySet() {
return map.entrySet();
}
}
You may use this map in anywhere where you use myMap
您可以在使用myMap
任何地方使用此地图
public class MyObject implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
String name;
int number;
ComparableMap myMap;
public MyObject(String name, int number, Map<String, String> myMap) {
this.name = name;
this.number = number;
this.myMap = new ComparablemyMap(myMap);
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final MyComplexObj myComplexObj = (MyComplexObj) o;
return myMap != null ? myMap.equals(myComplexObj.myMap) : myComplexObj.myMap == null;
}
@Override
public int hashCode() {
return myMap != null ? myMap.hashCode() : 0;
}
public int compareTo(MyComplexObj o) {
return myMap.compareTo(o.getMyMap())); //now it works
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.