2016-07-09 19 views
7

Der folgende Code ist eine bearbeitete Version von Dave Koelle's AlphanumComparator. Die Bearbeitung enthält Code, der leere Strings an das Ende der Liste oder den unteren Rand der JTable in meinem Fall sortiert. Das Problem ist ein java.lang.IllegalArgumentException: Comparison method violates its general contract! tritt auf.Vergleicher, der den allgemeinen Vertrag verletzt

Um mein Problem zu beheben, schaute ich hinein und fand Gründe wie der Komparator return 0; an der richtigen Stelle nicht. Ich fand auch einen Kommentar in der Java bug database die

Der Sortieralgorithmus von java.util.Arrays.sort zu lesen und (indirekt) durch java.util.Collections.sort ersetzt. Die neue Sortierimplementierung kann eine IllegalArgumentException auslösen, wenn sie eine Vergleichstabelle entdeckt, die den Vergleichsvertrag verletzt. Die vorherige Implementierung ignorierte eine solche Situation stillschweigend. Wenn das vorherige Verhalten gewünscht ist, können Sie die neue Systemeigenschaft verwenden, java.util.Arrays.useLegacyMergeSort,

vorheriges mergesort Verhalten
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; 
       } 
      } 
     } 
    } 
} 
wiederherzustellen

Würde jemand in der Lage sein, mein Problem zu helfen, zu beheben und erklären, warum dieser Komparator verletzt den vergleichbaren Vertrag

Ausnahme-Stack-Trace benötigt, wenn

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

haben Sie Beispiele für Eingabe? – Tibrogargan

Antwort

6

Ich denke, das Problem ist, dass Ihr Code nie s2Length untersucht, wenn s1Length Null ist. Sie müssen erneut die Kontrolle hinzufügen, wenn beide Strings leer sind, um zu sehen, wie folgt aus:

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; 
    } 
} 

Ihre aktuelle Implementierung gibt 1 oder -1 selbst dann, wenn die beiden Strings leer sind (dh sie müssen gleich vergleichen und das Rück Null) . Neuer Sortieralgorithmus erkennt dieses Problem und löst eine Ausnahme aus.

Hinweis:

Sie sollten diesen Code weiter vereinfachen können, indem swapInt eine int machen, die entweder 1 oder -1 ist je nach getSortOrder Ergebnis:

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

Ihre alternative Lösung gibt immer noch 1 oder -1 zurück, sobald s1 leer ist, ohne checkijg, wenn s2 ebenfalls leer ist. –

+0

Leider behebt dies das Problem nicht. Ich nehme an, dass dies auf das zurückzuführen ist, was @JBNizet gesagt hat. – Dan

+0

@Dan Bearbeitet. Vielen Dank! – dasblinkenlight

2

Ihr Komparator tut:

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; 
     } 
    } 
} 

Wenn also der Block if nicht eingegeben wird, bedeutet dies, dass mindestens eine Zeichenfolge leer ist. Aber beide könnten leer sein. Aber Ihr Komparator gibt in diesem Fall nur -1 oder 1 zurück. Das heißt, wenn A und B beide leer sind und der Vergleich von A mit B zu -1 führt, dann führt der Vergleich von B mit A auch zu -1, und A ist somit gleichzeitig kleiner und größer als B.

starten Sie Ihre anderen Block mit

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

Danke für die Antwort – Dan