2016-07-09 1 views
7

Le code ci-dessous est une version éditée de Dave Koelle's AlphanumComparator. L'édition contient du code qui trie les chaînes vides à la fin de la liste, ou au bas de la JTable dans mon cas. Le problème est java.lang.IllegalArgumentException: Comparison method violates its general contract! se produit.Contrat de violation de contrat général

Pour résoudre mon problème, je l'ai examiné et trouvé des raisons telles que le comparateur n'a pas un return 0; au bon endroit. J'ai aussi trouvé un commentaire dans Java qui bug database lu

L'algorithme de tri utilisé par java.util.Arrays.sort et (indirectement) par java.util.Collections.sort a été remplacé. La nouvelle implémentation de tri peut lancer une exception IllegalArgumentException si elle détecte un objet comparable qui viole le contrat comparable. L'implémentation précédente ignorait silencieusement une telle situation. Si le comportement précédent est désiré, vous pouvez utiliser la nouvelle propriété du système, java.util.Arrays.useLegacyMergeSort, pour rétablir le comportement précédent mergesort

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

Quelqu'un capable d'aider à résoudre mon problème et expliquer pourquoi ce comparateur viole le contrat comparable

Exception trace de pile si nécessaire

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

avez-vous des exemples d'entrée? – Tibrogargan

Répondre

6

Je pense que le problème est que votre code n'examine jamais s2Length lorsque s1Length est zéro. Vous devez ajouter une autre vérification pour voir si les deux chaînes sont vides, comme ceci:

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

Votre retourne la mise en œuvre actuelle 1 ou -1 même lorsque les deux chaînes sont vides (ce qui signifie qu'ils doivent comme égales et retour à zéro) . Un nouvel algorithme de tri détecte ce problème et déclenche une exception.

Note:

Vous devriez être en mesure de simplifier le code plus loin en faisant swapInt un int qui est soit 1 ou -1, selon le getSortOrder résultat:

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

Votre solution alternative renvoie encore 1 ou -1 dès que s1 est vide, sans vérification si s2 est vide, aussi. –

+0

Malheureusement, cela ne résout pas le problème. Je suppose que cela est dû à ce que @JBNizet a dit. – Dan

+0

@Dan Modifié. Merci! – dasblinkenlight

2

Votre comparateur fait:

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

Donc, s'il ne rentre pas dans le bloc if, cela signifie qu'au moins une chaîne est vide. Mais les deux pourraient être vides. Mais votre comparateur ne renvoie que -1 ou 1 dans ce cas. Ce qui signifie que si A et B sont tous les deux vides, et comparant A à B conduit à -1, alors comparer B à A mènera également à -1, et A est donc à la fois plus petit et plus grand que B en même temps.

Il suffit de commencer votre bloc autre avec

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

Merci pour la réponse – Dan