Java 实现Comparable接口踩坑记录

来源:互联网 发布:数据库开发工具有哪些 编辑:程序博客网 时间:2024/06/07 03:48

好久没有写代码了,最近尝试写个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;    }}

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

0 0