这个比较器可能有什么问题?

时间:2014-03-10 16:55:08

标签: java comparator

我得到了

  

“线程中的异常”AWT-EventQueue-0“
  java.lang.IllegalArgumentException:比较方法违反了它   总合同!“

此比较器的错误。它用于PriorityQueue

public class NodeFPQComp implements Comparator<Node> {

    @Override
    public int compare(Node arg0, Node arg1) {
        int result=0;

        if (arg0.getF() - arg1.getF() > 0) result = 1;
        if (arg0.getF() - arg1.getF() < 0) result = -1;

        return result;  
    }

}

getF()float值,永远不会接近限制,因此此处不会出现溢出问题。
它只返回另外两个浮点数的总和:

public float getG() {
        return this.G;
    }

    public float getH() {
        return this.H;
    }

    public float getF() {
        return getG() + getH();
    }

以下是使用它的队列:

openList = new PriorityQueue<Node>((int) gridSizeX()*gridSizeY()/10, new NodeFPQComp());

我想要的是让队列(A-Star路径查找)接受具有相同值(相同的f值)但具有不同身份(地图上的不同节点/方块)的多个对象。

奇怪的是路径查找仍然有效,但为什么我得到例外?那个比较器怎么可能不起作用?我错过了什么?可能是因为在将对象添加到队列之后更改getF()所依赖的值(这种情况在一个星形中发生了很多)会以某种方式弄乱它吗?

这是完整的堆栈跟踪:

Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: Comparison method violates its general contract!
    at java.util.TimSort.mergeLo(Unknown Source)
    at java.util.TimSort.mergeAt(Unknown Source)
    at java.util.TimSort.mergeCollapse(Unknown Source)
    at java.util.TimSort.sort(Unknown Source)
    at java.util.TimSort.sort(Unknown Source)
    at java.util.Arrays.sort(Unknown Source)
    at java.util.Collections.sort(Unknown Source)
    at javax.swing.SortingFocusTraversalPolicy.enumerateAndSortCycle(Unknown Source)
    at javax.swing.SortingFocusTraversalPolicy.getFocusTraversalCycle(Unknown Source)
    at javax.swing.SortingFocusTraversalPolicy.getFirstComponent(Unknown Source)
    at javax.swing.LayoutFocusTraversalPolicy.getFirstComponent(Unknown Source)
    at javax.swing.SortingFocusTraversalPolicy.getDefaultComponent(Unknown Source)
    at java.awt.FocusTraversalPolicy.getInitialComponent(Unknown Source)
    at java.awt.DefaultKeyboardFocusManager.dispatchEvent(Unknown Source)
    at java.awt.Component.dispatchEventImpl(Unknown Source)
    at java.awt.Container.dispatchEventImpl(Unknown Source)
    at java.awt.Window.dispatchEventImpl(Unknown Source)
    at java.awt.Component.dispatchEvent(Unknown Source)
    at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
    at java.awt.EventQueue.access$200(Unknown Source)
    at java.awt.EventQueue$3.run(Unknown Source)
    at java.awt.EventQueue$3.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
    at java.awt.EventQueue$4.run(Unknown Source)
    at java.awt.EventQueue$4.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
    at java.awt.EventQueue.dispatchEvent(Unknown Source)
    at java.awt.SequencedEvent.dispatch(Unknown Source)
    at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
    at java.awt.EventQueue.access$200(Unknown Source)
    at java.awt.EventQueue$3.run(Unknown Source)
    at java.awt.EventQueue$3.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
    at java.awt.EventQueue$4.run(Unknown Source)
    at java.awt.EventQueue$4.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
    at java.awt.EventQueue.dispatchEvent(Unknown Source)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.run(Unknown Source)

2 个答案:

答案 0 :(得分:2)

“合同”表示compare方法应为总排序。 (编辑)这个 也应该与equals(Object obj)方法一致(即使它可能没有严格强加),以确保数据结构(如TreeSet,TreeMap)的行为符合您的预期,因为它们可能基于同意 equals方法。

确保你有这些。

总排序意味着

    每个a.compareTo(a) >= 0
  • a
  • a.compareTo(b) >= 0b.compareTo(a) >= 0表示每a ba,b“相等”;这意味着a.compareTo(b) == 0,还有b.compareTo(a) == 0
  • a.compareTo(b) >= 0b.compareTo(c) >= 0暗示a.compareTo(c) >= 0,每a,b,c

很容易给出反例,其中compare方法返回{0,1,-1}且与总排序一致,因此请特别检查。

答案 1 :(得分:0)

这里有两种可能的失败,这两种失败都可以忽略不计。

第一个可能的失败是arg0或arg1可能为null,在这种情况下,你最终会抛出NullPointerException。这可能是可以接受的,因为很多时候你不会在集合中允许空值。

其他可能的失败是getF()返回某些整数类型(int,byte,long),如果返回值两个getF()调用具有足够大的差异,它们会溢出。

这种比较的最大有效差异取决于类型,对于byte和int,可以是Integer.MAX_VALUE,对于long,可以是Long.MAX_VALUE。

由于您已经在比较减法的结果,为什么不直接比较这两个值?

int result = 0;

if (arg0 != arg1) {
   if (arg0 == null) { 
      result = -1;
   } else if (arg1 == null) {
      result = 1;
   }
   if (arg0.getF() < arg1.getF()) {
      result = -1;
   }
   if (arg0.getF() > arg1.getF()) {
      result = 1;
   }
}

return result;

如果您更喜欢简洁的代码:

return arg0 != null && arg1 != null ? 
    Integer.compare(arg0.getF(), arg1.getF()) : // Neither are null, compare vlues
      arg0 == arg1 ? 
         0 : // both are null, return 0
         arg0 == null ? -1 : 1;  // arg0 != arg1, so one of them must be null.