我正在Java中实现一个A *算法,我使用TreeSet作为保持打开列表排序的简单方法。如果你不熟悉A *,那么它基本上是一个从A到B获得最短路径的函数,而开放列表是根据它们与B的接近度排序的节点列表(在我的例子中是Tiles
)。
我的对象实现了compareTo()
函数的排序,如下所示:
@Override
public int compareTo( Tile b )
{
return ( this.f< b.f) ? -1 : ( this.f> b.f) ? 1 : 0;
}
当我尝试将一些切片添加到打开列表时,我的问题出现了 - TreeSet似乎使用compareTo()
来检查对象是否已经存在,而不是equals()
。由于两个不同的Tiles
可能具有相同的f
值,因此TreeSet认为该对象已存在于列表中并且不会添加它。
根据文档(或至少我如何阅读),它应该使用equals
:
“如果指定的元素不存在,则将其添加到此集合。更正式地,如果集合中不包含元素e2,则将指定的元素e添加到此集合中,以便(e == null?e2 == null:e.equals(e2))。“ (强调我的)。
如何在调用equals()
或add()
和contains()
进行排序时让TreeSet使用compareTo()
?有关信息,我的Tile
类不会覆盖equals()
函数,因此它应该是默认的return a == b
。
如果使用TreeSet无法实现我的目标,那么我应该使用的正确集合是什么?
答案 0 :(得分:6)
这是预期的行为,根据TreeSet文档:
请注意,如果要正确实现Set接口,则由set维护的排序(无论是否提供显式比较器)必须与equals一致。 (有关与equals一致的精确定义,请参阅Comparable或Comparator。)这是因为Set接口是根据equals操作定义的,但TreeSet实例使用compareTo(或compare)方法执行所有元素比较,因此从集合的角度来看,通过这种方法被认为相等的元素是相等的。集合的行为即使其排序与equals不一致也是明确定义的;它只是没有遵守Set接口的一般合同。
在致电TreeSet
或equals
时,您无法让add
使用contains
。您最好的选择是通过compareTo
属性以及equals
关注的其他任何属性来使f
方法与equals
保持一致。
答案 1 :(得分:0)
我希望以下示例可以帮助SO用户。我有类似的情况,我处理的方式是除了价值之外还使用id。就我而言,价值指的是一个频率。
请注意使用'paddedText' - 如果你进行字符串比较,这是很重要的其他3> 11。
public int compareTo(Object o) {
String oText= o.getValue()+"";
oText = String.format("%20s",oText).replace(' ', '0')+o.getName();
paddedText = this.value +"";
paddedText=String.format("%20s",paddedText).replace(' ', '0') + this.name;
return oText.compareTo(paddedText);
}
答案 2 :(得分:0)
这里的答案已经让我创造了这个。我会留在这里,因为似乎只有解决方案的要点。
@Override
public int compare(TileState lhs, TileState rhs) {
int compare = (lhs.getTaxicab + lhs.mMoves.size())
- (rhs.getTaxicab + rhs.mMoves.size());
if (compare == 0 && !lhs.equals(rhs)) return 1;
return compare;
}
比较方法返回0时,您只需要担心。
如果是这种情况并且equals将返回false,则返回0以外的任何内容。