2016-07-09 34 views
7

Aşağıdaki kod, Dave Koelle's AlphanumComparator'un düzenlenmiş bir sürümüdür. Düzenleme, boş dizeleri listenin sonuna veya durumumdaki JTable öğesinin sonuna ayıran kod içerir. Sorun, bir java.lang.IllegalArgumentException: Comparison method violates its general contract! oluşur.Karşılaştırıcı ihlali genel sözleşmesi

Sorunumu gidermek için baktım ve karşılaştırıcı gibi nedenlerin doğru yerde return 0; bulunmadığını fark ettim. Ben de

java.util.Arrays.sort tarafından kullanılan sıralama algoritması okuyup (dolaylı olarak) java.util.Collections.sort almıştır Java bug database bir yorum bulunamadı. Yeni sıralama uygulaması, Karşılaştırılabilir sözleşmeyi ihlal eden bir Karşılaştırma algıladığında bir IllegalArgumentException atayabilir. Önceki uygulama böyle bir durumu sessizce görmezden geldi. Önceki davranış isteniyorsa , önceki mergesort Davranışı

geri, yeni sistem özelliğini, java.util.Arrays.useLegacyMergeSort kullanabilirsiniz
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; 
       } 
      } 
     } 
    } 
} 

birisi benim sorunu çözmek ve nedenini belirtin yardımcı olur gerekirse, bu karşılaştırma karşılaştırılabilir Sonuç

durum yığın izlemesi ihlal

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

ile başka blok başlar? – Tibrogargan

cevap

6

Sorun şu ki, kodunuz s1Length sıfır olduğunda asla s2Length incelememektedir.

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

Geçerli uygulama iki dizeleri (bunlar eşit karşılaştırmak ve sıfır dönmelidir yani) boş olduğunda bile 1 veya -1 döndürür: Bunu, her iki dizeleri boş olup olmadığını görmek için başka bir çek eklemem gerekiyor . Yeni sıralama algoritması bu sorunu algılar ve bir istisna atar.

Not:

Sen swapIntgetSortOrder sonucuna göre ya 1 veya -1, bir int yaparak daha da bu kodu basitleştirmek mümkün olmalıdır:

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

Alternatif çözümünüz, s1 boşsa, hala s2 boşsa, checkijg olmadan 1 veya -1 döndürür. –

+0

Ne yazık ki bu problemi çözmez. Bunun, @JBNizet'in söylediği şeyden kaynaklandığını farz ediyorum. – Dan

+0

@Dan Düzenlendi. Teşekkürler! – dasblinkenlight

2

Sizin karşılaştırıcı yapar:

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

Yani, if bloğuna girmezse, en az bir dizenin boş olduğu anlamına gelir. Ama ikisi de boş olabilir. Fakat karşılaştırıcınız bu durumda sadece -1 veya 1 döndürür. Yani, A ve B'nin ikisi de boşsa ve A ile B'yi karşılaştırmak -1'e yol açarsa, B'yi A'ya karşılaştırmak da -1'e neden olur ve A aynı zamanda hem B'den küçüktür hem de B'den daha büyüktür.

Sadece giriş örnekler var mı

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

Cevabınız için teşekkür ederiz – Dan

İlgili konular