我有一个等级(根据equals()
)必须由对象标识定义的类,即this == other
。
我想实现Comparable
来订购这样的对象(比如一些getName()
属性)。为了与equals()
保持一致,compareTo()
不得返回0
,即使两个对象具有相同的名称。
有没有办法比较compareTo
意义上的对象身份?我可以比较System.identityHashCode(o)
,但在哈希碰撞的情况下仍会返回0
。
我认为这里真正的答案是:不要实现Comparable。实现此接口意味着您的对象具有自然顺序。当你跟进那个想法时,那些“平等”的东西应该在同一个地方。
如果有的话,你应该使用自定义比较器...但即使这样也没有多大意义。如果定义<b ...的东西不允许给你一个== b(当a和b根据你的<关系“相等”时),那么整个比较方法就会因你的用例而被破坏。
换句话说:仅仅因为你可以将代码放入一个“以某种方式”产生你想要的东西的类......这样做并不是一个好主意。
根据定义,通过为每个对象分配Universally unique identifier(UUID)(或全球唯一标识符,(GUID))作为其标识属性,UUID具有可比性,并且与equals一致。 Java已经有了一个UUID类,一旦生成,你就可以使用字符串表示来实现持久性。专用属性还将确保身份在版本/线程/机器之间保持稳定。如果您有一种确保所有内容都获得唯一ID的方法,您也可以使用递增ID,但使用标准UUID实现将保护您免受集合合并和并行系统同时生成数据的问题。
如果您使用其他任何可比较的东西,则意味着它在与其身份/值分开的方式上具有可比性。因此,您需要为此对象定义可比较的方法,并记录该对象。例如,人们可以按名称,DOB,身高或优先顺序组合进行比较;最自然地通过名称作为约定(为了更容易被人类查找),如果两个人是同一个人则是分开的。你还必须接受compareto和equals是不相交的,因为它们基于不同的东西。
您可以添加第二个属性(比如int id
或long id
),它对于您的类的每个实例都是唯一的(您可以使用static
计数器变量并使用它来初始化构造函数中的id
)。
然后你的compareTo
方法可以首先比较名称,如果名称相等,比较id
s。
由于每个实例都有不同的id
,compareTo
永远不会返回0
。
虽然我坚持原来的答案,你应该使用UUID属性来进行稳定和一致的比较/平等设置,但我想我会继续回答一个问题:“如果你真的是偏执狂想要你能走多远保证唯一身份的可比“。
基本上,简而言之,如果你不相信UUID的唯一性或身份的唯一性,那么只需使用尽可能多的UUID来证明上帝正在积极地攻击你。 (请注意,虽然技术上不保证不抛出异常,但在任何理智的宇宙中需要2 UUID应该是矫枉过正的。)
import java.time.Instant;
import java.util.ArrayList;
import java.util.UUID;
public class Test implements Comparable<Test>{
private final UUID antiCollisionProp = UUID.randomUUID();
private final ArrayList<UUID> antiuniverseProp = new ArrayList<UUID>();
private UUID getParanoiaLevelId(int i) {
while(antiuniverseProp.size() < i) {
antiuniverseProp.add(UUID.randomUUID());
}
return antiuniverseProp.get(i);
}
@Override
public int compareTo(Test o) {
if(this == o)
return 0;
int temp = System.identityHashCode(this) - System.identityHashCode(o);
if(temp != 0)
return temp;
//If the universe hates you
temp = this.antiCollisionProp.compareTo(o.antiCollisionProp);
if(temp != 0)
return temp;
//If the universe is activly out to get you
temp = System.identityHashCode(this.antiCollisionProp) - System.identityHashCode(o.antiCollisionProp);;
if(temp != 0)
return temp;
for(int i = 0; i < Integer.MAX_VALUE; i++) {
UUID id1 = this.getParanoiaLevelId(i);
UUID id2 = o.getParanoiaLevelId(i);
temp = id1.compareTo(id2);
if(temp != 0)
return temp;
temp = System.identityHashCode(id1) - System.identityHashCode(id2);;
if(temp != 0)
return temp;
}
// If you reach this point, I have no idea what you did to deserve this
throw new IllegalStateException("RAGNAROK HAS COME! THE MIDGARD SERPENT AWAKENS!");
}
}
假设有两个具有相同名称的对象,如果equals()
返回false
,那么compareTo()
不应该返回0.如果这是你想要做的,那么下面可以帮助:
hashcode()
并确保它不仅仅依赖于name
compareTo()
如下:public void compareTo(MyObject object) {
this.equals(object) ? this.hashcode() - object.hashcode() : this.getName().compareTo(object.getName());
}
你有独特的对象,但正如Eran所说,你可能需要一个额外的计数器/ rehash代码来处理任何碰撞。
private static Set<Pair<C, C> collisions = ...;
@Override
public boolean equals(C other) {
return this == other;
}
@Override
public int compareTo(C other) {
...
if (this == other) {
return 0
}
if (super.equals(other)) {
// Some stable order would be fine:
// return either -1 or 1
if (collisions.contains(new Pair(other, this)) {
return 1;
} else if (!collisions.contains(new Pair(this, other)) {
collisions.add(new Par(this, other));
}
return 1;
}
...
}
因此,请选择伊兰的答案,或将要求作为问题。