我正在尝试覆盖我HashSet
所提到的方法:
Set<MyObject> myObjectSet = new HashSet<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;
}
[...]
}
如何覆盖hashcode(),equals()和compareTo()方法?
目前我有以下内容:
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);
}
我读到通过id进行比较是不够的,这是对象是数据库的持久实体(参见here)。
此类型的所有对象的名称和编号都不是唯一的。
那我应该怎么覆盖呢? 我还需要比较里面的hashMap吗?
我很困惑。关于该对象的唯一独特之处是地图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()));
}
这在compareTo方法中失败,&#34;此方法未定义类型Map
答案 0 :(得分:1)
这是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;
}
}
但是,既然你说过
关于对象的唯一独特之处在于myMap得到的地图 在生命周期的后期填充。
我会保留myMap并跳过姓名和号码(但这引出了一个问题,为什么你要在你的收藏品的所有元素中加入冗余数据名和号码?)
然后变为
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;
}
}
请记住,equals和hashcode方法还有其他方法。例如,以下是intelliJ为代码生成提供的选项
与Equals和Hashcode不同,compareTo和任何其他行为之间不存在契约。你不需要对compareTo做任何事情,直到你想利用它来进行排序。要详细了解CompareTo Why should a Java class implement comparable?
答案 1 :(得分:1)
这里的基本问题是&#34;如何确定两个对象是否彼此相等?&#34;
对于简单对象,这是一个简单的问题。然而,即使是稍微复杂的物体也变得越来越困难。
如原始问题所述:
关于该对象的唯一独特之处是地图myMap将在生命周期的后期填充。
给定类型MyObject
的两个实例,必须将成员变量myMap
相互比较。此地图的类型为Map<String, String>
。立即想到几个问题:
每个问题的答案因应用程序而异。为了使其适用于一般受众,正在做出以下假设:
使用equals()
,hashCode()
和compareTo()
的好处是,一旦hashCode()
正确实施,其他功能可以基于hashCode()
定义
考虑到所有这些,我们有以下实现:
@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());
}
所有关键工作都在hashCode()
内完成。对于排序,compareTo()
函数只需要返回一个负数/零/正数 - 一个简单的hashCode()
差异。并且equals()
函数只需要返回true / false - 一个compareTo()
等于零的简单检查。
为了进一步阅读,刘易斯卡罗尔就逻辑基础进行了一次着名的对话,其中涉及平等的基本问题:
https://en.wikipedia.org/wiki/What_the_Tortoise_Said_to_Achilles
而且,就简单的语法结构而言,有一个很好的例子就是两个&#34;相等&#34; chapter 6, "Pig and Pepper"开头的句子,来自爱丽丝梦游仙境:
鱼足者开始从他的胳膊下面写下一封很棒的信,然后把它交给对方,用严肃的语气说,#34;对于公爵夫人。女王邀请她去玩槌球。&#34;青蛙步兵以同样庄严的语气重复着,#34;来自女王。邀请公爵夫人去玩槌球。&#34;然后他们鞠躬低沉,卷曲纠结在一起。
答案 2 :(得分:0)
compareTo()
与排序有关。它与HashSet
或HashMap
无关。
正常工作的equals()
和hashCode()
对基于哈希的集合的成员至关重要。请阅读Javadoc中Object
的说明。
实施这些建议的明确建议可能在Joshua Bloch的 Effective Java 中。我建议阅读相关章节 - 它很容易谷歌。试图在这里解释一切都没有意义。
您可能已经注意到的一件事是,您的字段myMap
有自己的equals()
和hashCode()
,因此您无需执行任何特殊操作它。如果你可以保证没有一个字段为空,那么合理的hashCode()
将是(遵循布洛赫的系统):
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;
}
(如果其中任何一个可能为null,则需要更多代码)
几乎所有IDE都会使用类中的所有字段自动生成equals()
和hashcode()
。他们将使用与Bloch的建议非常相似的东西。寻找用户界面。你会找到的。
另一种选择是使用Apache ReflectionUtils,它允许您简单地使用:
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
@Override
public boolean equals(final Object obj) {
return EqualsBuilder.reflectionEquals(this, obj);
}
这将确定在运行时使用哪些字段,并应用Bloch的方法。
答案 3 :(得分:0)
如果您想使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();
}
}
您可以在使用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
}
}