Java 实现Comparable接口踩坑记录

本文记录了在实现A*算法时使用Java的TreeSet数据结构遇到的问题。自定义类型实现Comparable接口时,不恰当的compareTo()方法导致了对象相等判断错误。在修复过程中,虽然避免了equals()与compareTo()不一致的问题,但又引入了新的问题,即TreeSet中出现了重复对象。最终了解到,正确的compareTo()方法应确保对象间的大小关系唯一确定,以保证二叉搜索树的正确性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

好久没有写代码了,最近尝试写个A*算法练练手,过程中遇到了一些问题记录下来。

由于A*算法在获取当前格的时候取一个集合中的最小值,所以尝试使用TreeSet作为数据结构。
自定义类型实现了Comparable接口用以存放进TreeSet。

而实现的compareTo(Object o)方法的定义实在需要谨慎,因为我就在这遇到了一个毁天灭地大问题。

我的目标是让一个自定义类型根据某个变量的大小进行排序,想当然写了如下代码:

public int compareTo(Apath apath) {
    // TODO 自动生成的方法存根
    if(F>apath.getF()){
        return 1;
    }else if(F<apath.getF()){
        return -1;
    }else {
        return 0;
    }
}

问题来了,return 0意味着两个对象相等,比如我这里使用的TreeSet调用contains(Object o)方法时,两个不同对象的F相同时会当成同一个对象。愚蠢。

接下来一拍脑袋,加了一点东西,以为这样就没事了。

public int compareTo(Apath apath) {
    // TODO 自动生成的方法存根
    if(F>apath.getF()){
        return 1;
    }else if(F<apath.getF()){
        return -1;
    }else if(this == apath){
        return 0;
    }else {
        return 1;
    }
}

这样子两个相同的对象才会真正被当成相同的处理,正如这句话所说。

强烈推荐(虽然不是必需的)使自然排序与 equals 一致。所谓与equals一致是指对于类 C 的每一个 e1 和 e2 来说,当且仅当 (e1.compareTo((Object)e2) == 0) 与e1.equals((Object)e2) 具有相同的布尔值时,类 C 的自然排序才叫做与 equals 一致 。

这下好了吧?看似Bug消灭了,其实只是隐藏的更深了!!

这时候我发现有时候TreeSet中会包含多个一样的对象,即contains(Object o)方法失效。找来找去,发现还是在自定义的CompareTo(Object o)函数的问题上。

通过了解,我知道了实际上TreeSet是二叉树实现,那么使用的必须是二叉搜索了。上面的代码在最后一个else里胡乱丢了一个return 1 ,实际上这棵树有可能是乱的。因为当两个对象F相同时,他们谁大于谁是不确定的!

你可以想象:
当 a1.F == a2.F
a1.compareTo(a2)==1
a2.compareTo(a1)==1
也就导致后来的有关二叉搜索的函数都乱套了。

最后改成了这样。所有对象之间的大小关系唯一确定才行。
说起来简单,马马虎虎导致的问题。

public int compareTo(Apath apath) {
    // TODO 自动生成的方法存根
    if(F>apath.getF()){
        return 1;
    }else if(F<apath.getF()){
        return -1;
    }else if(this == apath){
        return 0;
    }else if(this.hashCode() > apath.hashCode()){
        return 1;
    }else {
        return -1;
    }
}

这样一来,即使两个对象的参数完全一样,都能比较出大小了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值