2016-07-09 2 views
7

Данный код является отредактированной версией Dave Koelle's AlphanumComparator. Редактирование содержит код, который сортирует пустые строки в конце списка или в нижней части JTable в моем случае. Возникает проблема: java.lang.IllegalArgumentException: Comparison method violates its general contract!.Компаратор, нарушающий общий договор

Чтобы исправить свою проблему, я просмотрел ее и нашел, что такие причины, как компаратор, не имеют return 0; в правильном месте. Я также нашел комментарий в Java bug database гласивший

Алгоритм сортировки, используемый java.util.Arrays.sort и (косвенно) java.util.Collections.sort был заменен. Новая реализация sort может вызывать исключение IllegalArgumentException, если оно обнаруживает сравнимое, которое нарушает договор Comparable. Предыдущая реализация молча игнорировала такую ​​ситуацию. Если предыдущее поведение желательно, вы можете использовать новую систему собственность, java.util.Arrays.useLegacyMergeSort, чтобы восстановить предыдущее поведение

слияния
import java.util.Comparator; 
import javax.swing.JTable; 
import javax.swing.SortOrder; 

public class AlphanumComparator implements Comparator<String> { 
    JTable table; 

    public AlphanumComparator(JTable table) { 
     this.table = table; 
    } 

    private final boolean isDigit(char ch) { 
     return ch >= 48 && ch <= 57; 
    } 

    private final String getChunk(String s, int slength, int marker) { 
     StringBuilder chunk = new StringBuilder(); 
     char c = s.charAt(marker); 
     chunk.append(c); 
     marker++; 
     if (isDigit(c)) { 
      while (marker < slength) { 
       c = s.charAt(marker); 
       if (!isDigit(c)) 
        break; 
       chunk.append(c); 
       marker++; 
      } 
     } else { 
      while (marker < slength) { 
       c = s.charAt(marker); 
       if (isDigit(c)) 
        break; 
       chunk.append(c); 
       marker++; 
      } 
     } 
     return chunk.toString(); 
    } 

    public int compare(String s1, String s2) { 
     boolean swapInt = table.getRowSorter().getSortKeys().get(0).getSortOrder() == SortOrder.ASCENDING; 

     int thisMarker = 0; 
     int thatMarker = 0; 
     int s1Length = s1.length(); 
     int s2Length = s2.length(); 

     if(s1Length != 0 && s2Length != 0) { 
      while (thisMarker < s1Length && thatMarker < s2Length) { 
       String thisChunk = getChunk(s1, s1Length, thisMarker); 
       thisMarker += thisChunk.length(); 

       String thatChunk = getChunk(s2, s2Length, thatMarker); 
       thatMarker += thatChunk.length(); 

       int result = 0; 
       if (isDigit(thisChunk.charAt(0)) && isDigit(thatChunk.charAt(0))) { 
        int thisChunkLength = thisChunk.length(); 
        result = thisChunkLength - thatChunk.length(); 
        if (result == 0) { 
         for (int i = 0; i < thisChunkLength; i++) { 
          result = thisChunk.charAt(i) - thatChunk.charAt(i); 
          if (result != 0) { 
           return result; 
          } 
         } 
        } 
       } else { 
        result = thisChunk.compareTo(thatChunk); 
       } 

       if (result != 0) 
        return result; 
      } 

      return s1Length - s2Length; 
     } else { 
      if(swapInt) { 
       if(s1Length == 0) { 
        return 1; 
       } else { 
        return -1; 
       } 
      } else { 
       if(s1Length == 0) { 
        return -1; 
       } else { 
        return 1; 
       } 
      } 
     } 
    } 
} 

ли кто-то сможет помочь исправить мою проблему и объяснить, почему этот компаратор нарушает Сопоставимый контракт

Исключение трассировки стека при необходимости

Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: Comparison method violates its general contract! 
    at java.util.ComparableTimSort.mergeLo(ComparableTimSort.java:744) 
    at java.util.ComparableTimSort.mergeAt(ComparableTimSort.java:481) 
    at java.util.ComparableTimSort.mergeForceCollapse(ComparableTimSort.java:422) 
    at java.util.ComparableTimSort.sort(ComparableTimSort.java:222) 
    at java.util.Arrays.sort(Arrays.java:1246) 
    at javax.swing.DefaultRowSorter.sort(DefaultRowSorter.java:607) 
    at javax.swing.DefaultRowSorter.setSortKeys(DefaultRowSorter.java:319) 
    at javax.swing.DefaultRowSorter.toggleSortOrder(DefaultRowSorter.java:480) 
    at javax.swing.plaf.basic.BasicTableHeaderUI$MouseInputHandler.mouseClicked(BasicTableHeaderUI.java:112) 
    at java.awt.AWTEventMulticaster.mouseClicked(AWTEventMulticaster.java:270) 
    at java.awt.Component.processMouseEvent(Component.java:6538) 
    at javax.swing.JComponent.processMouseEvent(JComponent.java:3324) 
    at java.awt.Component.processEvent(Component.java:6300) 
    at java.awt.Container.processEvent(Container.java:2236) 
    at java.awt.Component.dispatchEventImpl(Component.java:4891) 
    at java.awt.Container.dispatchEventImpl(Container.java:2294) 
    at java.awt.Component.dispatchEvent(Component.java:4713) 
    at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4888) 
    at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4534) 
    at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4466) 
    at java.awt.Container.dispatchEventImpl(Container.java:2280) 
    at java.awt.Window.dispatchEventImpl(Window.java:2750) 
    at java.awt.Component.dispatchEvent(Component.java:4713) 
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758) 
    at java.awt.EventQueue.access$500(EventQueue.java:97) 
    at java.awt.EventQueue$3.run(EventQueue.java:709) 
    at java.awt.EventQueue$3.run(EventQueue.java:703) 
    at java.security.AccessController.doPrivileged(Native Method) 
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76) 
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86) 
    at java.awt.EventQueue$4.run(EventQueue.java:731) 
    at java.awt.EventQueue$4.run(EventQueue.java:729) 
    at java.security.AccessController.doPrivileged(Native Method) 
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76) 
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:728) 
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201) 
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116) 
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105) 
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101) 
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93) 
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:82) 
+0

у вас есть примеры ввода? – Tibrogargan

ответ

6

Я думаю, проблема заключается в том, что ваш код никогда не проверяет s2Length, когда s1Length равен нулю. Вам нужно добавить еще одну проверку, чтобы увидеть, если обе строки являются пустыми, как это:

if(swapInt) { 
    if(s1Length == 0 && s2Length != 0) { 
     return 1; 
    } else if (s2Length == 0 && s1Length != 0) { 
     return -1; 
    } else { 
     return 0; 
    } 
} else { 
    if(s1Length == 0 && s2Length != 0) { 
     return -1; 
    } else if (s2Length == 0 && s1Length != 0) { 
     return 1; 
    } else { 
     return 0; 
    } 
} 

Ваше текущее выполнение возвращает 1 или -1 даже тогда, когда две строки пустые (что означает, что они должны сравнить равными и возвращать ноль) , Новый алгоритм сортировки обнаруживает эту проблему и выдает исключение.

Примечание:

Вы должны быть в состоянии упростить этот код дальше, сделав swapInt в int, который либо 1 или -1, в зависимости от getSortOrder результата:

if(s1Length == 0 && s2Length != 0) { 
    return swapInt; 
} else if (s2Length == 0 && s1Length != 0) { 
    return -swapInt; 
} else { 
    return 0; 
} 
+1

Ваше альтернативное решение все еще возвращает 1 или -1, как только s1 пуст, без checkijg, если s2 также пуст. –

+0

К сожалению, это не устраняет проблему. Я предполагаю, что это связано с тем, что сказал @JBNizet. – Dan

+0

@ Dan Edited. Благодаря! – dasblinkenlight

2

Ваш компаратор делает:

if (s1Length != 0 && s2Length != 0) { 
    ... 
} else { 
    if (swapInt) { 
     if(s1Length == 0) { 
      return 1; 
     } else { 
      return -1; 
     } 
    } else { 
     if(s1Length == 0) { 
      return -1; 
     } else { 
      return 1; 
     } 
    } 
} 

Итак, если он не вводит блок if, это означает, что хотя бы одна строка пуста. Но оба могут быть пустыми. Но ваш компаратор только возвращает -1 или 1 в этом случае. Это означает, что если A и B оба пустые, а сравнение A с B приводит к -1, то сравнение B с A также приведет к -1, и тогда A будет одновременно меньше и больше B.

Просто начните еще блок с

if (s1Length == 0 && s2Length == 0) { 
    return 0; 
} 
+0

Спасибо за ответ – Dan